Merge pull request 'Designs 25, 26, 32, 51, 55: the registry milestone's designs after the review by architectural domain (ADR 0615-0621)' (#511) from docs/registry-milestone-designs into main
This commit was merged in pull request #511.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: hq-design-writer
|
||||
description: Writes one set of amendments to hq's to-be designs, glossary and playbooks from accepted decisions and a milestone's findings (hq playbook 02, amending a design; playbook 04 step 4), in a worktree on a branch of its own, ready for the review by architectural domain. Writes only in its worktree; never pushes.
|
||||
tools: Read, Grep, Glob, Bash, Write, Edit, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You amend hq's designs for the lead who started you. The caller's handover names the designs, the records
|
||||
they follow, the file that lists the findings to write in, and the branch. You work in a git worktree of hq under
|
||||
`.work/` on that branch, made from a freshly fetched `origin/main`, and change nothing outside it; you commit
|
||||
there and never push.
|
||||
|
||||
**First, what the mesh already knows.** Ask the knowledge base for every record the handover names
|
||||
(`knowledge-base.get` with `reasoning`, and `links`), and `knowledge-base.ask` for the area in plain words: a
|
||||
record you did not expect may already decide a point. Read each design whole before changing it, hq's glossary
|
||||
(`00-META/glossary.md`) and playbook 02's steps for amending a to-be design. A clause the records do not
|
||||
decide is not written into a design as if they did: say it to the caller as an open point.
|
||||
**It reads the mesh and writes nothing there.** It calls only verbs that read (`knowledge-base.get`, `ask`, `links`,
|
||||
`holds`; `issue-tracker.list`, `get`); never `knowledge-base.reserve` or `propose`, nor any `issue-tracker` verb that
|
||||
writes (`open`, `move`, `note`, `link`). What a record or an issue must carry goes to the caller in its report.
|
||||
|
||||
**Then write, by the rules of the record.** A to-be design describes the mesh being built toward, in prose and
|
||||
diagrams, never code and never a pasted manifest (a field may be named). Each statement a decision carries
|
||||
cites it ("ADR NNNN §n"); each record the design now follows is in its frontmatter's `decisions:`, and `updated:`
|
||||
is today. Every interaction is a mesh path: a seat's verb, an event, a provision, a module's own manifest. Speak
|
||||
of the seat first, the module second. A rule the design states says how it is checked. hq is public: role
|
||||
names, never node names, addresses or paths. A new word goes into the glossary with its definition and its
|
||||
*Not:* line in the same change; a retired word is never used.
|
||||
|
||||
**Then check.** Run `go test ./...` at the worktree's root (the records, index, cycle and words tests) and fix
|
||||
what it refuses. Commit in the imperative, about why.
|
||||
|
||||
Report to the caller in under 30 lines: the branch and its head, each file changed with the findings it now
|
||||
carries, what the tests said, and every point you left open because no record decides it, with your
|
||||
recommendation. Glossary words only; a guess is said as one.
|
||||
+33
-1
@@ -106,6 +106,9 @@ and repair).
|
||||
`mesh-contracts` for the seat contracts mesh-sdk held, `mesh-<area>-contracts` for an area's. A seat's holder
|
||||
implements the contract; the contracts module holds no seat (ADR 0608 §5).
|
||||
*Not:* ~~SDK module~~, ~~domain package~~
|
||||
- **contracts marker** — the field `generated-from` of a contracts module's package artifact, naming the directory
|
||||
of the seat definitions its files are generated from; the judge refuses a module carrying it that claims a seat,
|
||||
and the build modules' test step regenerates and compares every module carrying it (design 26).
|
||||
- **package series** — the first two numbers of a package's versions (`0.1`), declared in its manifest and raised
|
||||
only by a reviewed change, for a change that breaks its users; a user's manifest names the package series it
|
||||
accepts (ADR 0606 §4, ADR 0609 §1). Bare "series" only where the package is plain (see [Homonyms](#homonyms)).
|
||||
@@ -150,6 +153,13 @@ downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348.
|
||||
module has moved to their config names (ADR 0348 §1, issue 630).
|
||||
What a "flavor" once varied is a config value or a separate module.
|
||||
*Not:* ~~flavor~~
|
||||
- **rename window** — the time a renamed config value is read under both its names: from the merge of its
|
||||
`renamed-from` until every node-engine whose node has a want of the module reports applied a build reading the new
|
||||
name, away nodes included; mesh-config answers both names meanwhile, and its seat verb `renames` says each window's
|
||||
nodes (ADR 0609 §4, design 32 § 8).
|
||||
- **retiring value** — a config value or secret no manifest on any trunk reads any more and no node-engine runs a build
|
||||
reading; mesh-config asks the operator once whether to remove it, and removes it only on the Approve, a secret
|
||||
through the vault's `retire` (design 32 § 8). Not the provision sense of **retire** (Provisioning).
|
||||
- **config candidate** — the config declarations a pull request's head adds or changes against the trunk's
|
||||
manifests, kept by the indexer per open pull request in its state `config-candidates`; mesh-config judges
|
||||
it again for the values proposed for that pull request (ADR 0565 §2, §3).
|
||||
@@ -480,6 +490,11 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
|
||||
**composition check**; for a delivery group, one verdict on every member's head (ADR 0445, ADR 0447). Before
|
||||
release 4 of ADR 0429 a pull request carried two statuses, one for the modules the change touches and one
|
||||
for the repository's own script (ADR 0238).
|
||||
- **module graph** — the edges the indexer keeps between modules, from which each delivery part orders a source
|
||||
revision by its own rule: provisions, bus state, a served verb and a reader's capability are waited on; a package
|
||||
and a build module's change are triggers that rebuild, never waits; a verb edge on a cycle is left out (ADR 0603
|
||||
§1, §2, ADR 0614). Nothing stores the order.
|
||||
*Not:* ~~build graph~~
|
||||
- **composition check** — whether the machines a change touches still compose with it, run by the assigner
|
||||
from the modules the indexer and the builder hold, with mesh-sdk's composition, the one the controller
|
||||
composes with
|
||||
@@ -775,6 +790,17 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564.
|
||||
- **bus state** — what a module keeps on the bus, asked for in `requires` as a provision of the bus module, by a
|
||||
template and its options; created by the bus module and reached by the owner's account alone. Another module
|
||||
asks the owner's tools for its data, or hears its declared events (ADR 0595, ADR 0596).
|
||||
- **`bus-state`** — the provision of the bus module that every bus-state entry of a manifest requires, so the module
|
||||
graph orders the bus module's build before a module asking bus state (ADR 0614 §5). Provided by the holder of the
|
||||
seat `mesh-broker`.
|
||||
- **binding** of bus state — the names on the bus of a module's bus-state entries, which the grants part writes into
|
||||
the module's membership on the bus module's `created`, rewrites on `recreated` and removes on `dropped`; a module
|
||||
whose binding is missing at start fails loudly (ADR 0595 §1, §7). Never bare where it could be read as a sticky
|
||||
binding or a placement's bindings (see [Homonyms](#homonyms)).
|
||||
- **first view** — what a reader is answered once, at its start, by the owner's verb or tool, before it follows the
|
||||
owner's events; it never reads the owner's bus state instead, and asks again only on a gap in the events
|
||||
(ADR 0596 §2, ADR 0615 §3 to §5).
|
||||
*Not:* ~~initial view~~
|
||||
- **storage type** — how the bus keeps bus state: the **stream**, the base, or one built on it, the **key-value
|
||||
bucket**, the **object store** or the **ledger**, each with calls of its own (ADR 0595 §2).
|
||||
*Not:* ~~data type~~
|
||||
@@ -832,7 +858,7 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03
|
||||
**Uses:** operator, person, node, module, seat, tool (Module), proof and identity (Identity and access), release (Delivery).
|
||||
|
||||
- **mesh MCP server** — the endpoint on a node's loopback through which every agent and person on that
|
||||
node reaches the mesh: the MCP server named `mesh`, with its five tools `mesh_overview`,
|
||||
node reaches the mesh: the MCP server named `mesh`, with its tools, among them `mesh_overview`,
|
||||
`mesh_machine`, `mesh_search`, `mesh_describe` and `mesh_call`. It is the tool runner's loopback mode,
|
||||
not a module of its own. "Console" suggested a terminal or a shell, and the module `mesh-console` it
|
||||
once named no longer exists; "the tool bridge" and "the brain" named the predecessor's program.
|
||||
@@ -1027,6 +1053,9 @@ Not a domain of the mesh but of the way it is built, listed because its words me
|
||||
- **decision record** — one numbered record in the knowledge base, named "ADR NNNN", never rewritten:
|
||||
**superseded** by a later record, or corrected by a marked **progressive insight**. Always qualified in this repository: bare
|
||||
*record* has other meanings (see [Homonyms](#homonyms)).
|
||||
- **overview page** — the knowledge base's read-only web page for the operator: the designs in progress with their
|
||||
milestones and issues, the records accepted in the last seven days and those waiting, and what waits on the
|
||||
operator, built from its own ledger and three modules' verbs and events (ADR 0616, design 55 § 11).
|
||||
- **clause** — one numbered statement a decision record makes, with its own status (`proposed`, `in force`,
|
||||
`changed`, `superseded`): a later record may change one clause and leave the rest
|
||||
(ADR 0290). Not a *rule*, which is a line of
|
||||
@@ -1124,6 +1153,9 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
||||
| | the function in mesh-sdk that composes them, which the controller and the assigner both run | **mesh-sdk's composition** | Placement |
|
||||
| | whether the machines a change touches still compose | **composition check** | Delivery |
|
||||
| membership | the subjects a bus account is issued | **membership** | Provisioning |
|
||||
| binding | the names on the bus of a module's bus state, in its membership | **binding** of bus state | Data |
|
||||
| | a consumer's tie to the data a provider keeps for it | **sticky binding** | Data |
|
||||
| | which host ports and addresses a placement gives a module | a placement's **bindings** | Placement |
|
||||
| | an account in a group of the machine | **account group** | Placement |
|
||||
| domain | an area of the mesh that owns a set of concepts and the one word for each (ADR 0244), the sections of this page; the one sense "domain" keeps bare | **mesh domain** | The record |
|
||||
| | one of the eight concerns a direction and a design are reviewed against (playbook 01); never written bare | **architectural domain** | The record |
|
||||
|
||||
@@ -163,7 +163,8 @@ for the operator's Approve on a channel that proves who answered.
|
||||
## Closing
|
||||
|
||||
An effort never just stops. It closes through playbook [02](02-graduation.md) as `graduated` or
|
||||
`abandoned`, always with a `became` link to what it turned into. Nothing is deleted — what was rejected,
|
||||
`abandoned`: a graduated effort with a `became` link to what it turned into, an abandoned one with a `cites` link to
|
||||
what replaced it (playbook 02 step 5). Nothing is deleted — what was rejected,
|
||||
and why, is the more expensive half to rediscover.
|
||||
|
||||
## How it is checked
|
||||
|
||||
@@ -62,7 +62,10 @@ about an hour, and said to the operator if it runs past.
|
||||
milestones, issues, and each milestone built as one feature step (playbook [07](07-feature-branches.md)).
|
||||
5. **Close the effort.** `knowledge-base.propose` the effort again, whole, with `status: graduated` and a
|
||||
`became` link to the decision record (`"NNNN"`) and to the design document (its path). An effort that
|
||||
stops without a decision is proposed with `status: abandoned` and a `became` link to what replaced it.
|
||||
stops without a decision is proposed with `status: abandoned` and a `cites` link to what replaced it: the
|
||||
knowledge base refuses `became` from a research effort to another research effort (its link-fits rule, design
|
||||
55 § 2), so a replacing effort is cited, and `became` stays for the decision and the design an effort turned
|
||||
into.
|
||||
|
||||
## Writing a decision record
|
||||
|
||||
@@ -140,7 +143,9 @@ A design changes only through a decision.
|
||||
|
||||
1. Write the decision record (above). If it reverses an earlier one, it `supersedes` it.
|
||||
2. Edit the to-be design document, add the record to its `decisions:` as `"ADR NNNN"`, and set `updated:`
|
||||
to today. Review it by architectural domain before it is merged (playbook [01](01-research.md#review-by-architectural-domain)), and
|
||||
to today. A set of amendments across several designs (a milestone's findings, a graduation's) is written by the
|
||||
agent `hq-design-writer` (`.claude/agents/`), started with a handover naming the designs, the records and the
|
||||
branch. Review it by architectural domain before it is merged (playbook [01](01-research.md#review-by-architectural-domain)), and
|
||||
rewrite its *Reviewed by architectural domain* section.
|
||||
3. If the amendment came from an issue, give the issue the document path in the issue tracker:
|
||||
`amended-design` on its `issue-tracker.move` to `resolved`, or `issue-tracker.link` with kind
|
||||
|
||||
@@ -10,9 +10,15 @@ code:
|
||||
- mesh-tools node-tools/internal/bus (a module's state, ADR 0201; the new calls and apply, ADR 0595, ADR 0597)
|
||||
- mesh-sdk (the typed bus-state entry, its binding and the new calls' package, ADR 0595)
|
||||
- mesh-delivery (the judge's rules and the grants part's binding, ADR 0595)
|
||||
updated: 2026-11-03
|
||||
updated: 2026-11-04
|
||||
decisions:
|
||||
- "ADR 0595"
|
||||
- "ADR 0619"
|
||||
- "ADR 0615"
|
||||
- "ADR 0621"
|
||||
- "ADR 0614"
|
||||
- "ADR 0613"
|
||||
- "ADR 0612"
|
||||
- "ADR 0596"
|
||||
- "ADR 0597"
|
||||
- "ADR 0289"
|
||||
@@ -690,19 +696,51 @@ modules' names meet.
|
||||
the tool runner reads the membership ──▶ the module's calls reach its own entries, and only those
|
||||
```
|
||||
|
||||
- **The trigger is the judge's verdict** (ADR 0595 §4), heard as an event that carries the module's entries whole,
|
||||
so the bus module never reads the indexer's state and never polls. It hears the verdicts on a durable consumer, so
|
||||
one given while it was down is heard when it is back, and at start it runs `check` over every accepted manifest,
|
||||
creating what is missing. The judge checks every entry once, at the merge
|
||||
check and at take-in (ADR 0578; ADR 0595 §8).
|
||||
- **The trigger is the judge's verdict** (ADR 0595 §4), heard as the indexer's event `manifest-accepted`, which
|
||||
carries the module's entries whole, so the bus module never reads the indexer's state and never polls. The indexer
|
||||
says it for every newly accepted manifest, with its entries empty when it asks for none, and the bus module reads an
|
||||
empty one as "this module asks nothing now", which is what its orphans need. A module whose manifest leaves the
|
||||
trunk (its directory deleted) gets an empty `manifest-accepted` too, so its entries reach `orphans` and raise
|
||||
`orphaned`; a manifest the judge set aside to wait gets none, so its entries stay asked. The indexer's tool
|
||||
`indexer_say_accepted` says `manifest-accepted` again for one module by hand, from the verdict it holds, recorded as a
|
||||
hand act, for a verdict the bus module is thought to have missed. It goes, as every event of the
|
||||
indexer, on the module events stream (ADR 0363) under the indexer's own subjects. A publish of it that fails is
|
||||
published again, and one still failing writes the indexer's finding naming the module, cleared once said (today
|
||||
the indexer only logs it). It hears the verdicts on a durable consumer, so one given while it was down is heard
|
||||
when it is back. **Its first view** at start is the indexer's
|
||||
tool `indexer_bus_state` (every accepted manifest's bus-state entries, by module and digest), asked once; the
|
||||
events keep it current after that, and a stream that keeps a verdict only for its age is never its source. A
|
||||
verdict replayed from its consumer is applied only if newer than what the first view answered for that module, by
|
||||
the indexer's revision alone (a digest has no order), so a replay never takes an entry back. An `indexer_bus_state`
|
||||
that does not answer makes the bus module's health unhealthy, naming the verb, and is asked again on a bounded
|
||||
backoff (from 10 seconds, doubling, at most every 5 minutes); nothing is created or orphaned meanwhile. Beside the
|
||||
backoff, the bus module's tool `nats_first_view_again` asks `indexer_bus_state` at once by hand, recorded as a hand
|
||||
act. It keeps
|
||||
what it last heard per module in its own state `asked` (an old `state` section, its own kept data staying there,
|
||||
ADR 0595 §10), from which `list`, `check` and `orphans` answer; a key goes only on an empty `manifest-accepted` for
|
||||
the module, never because a manifest the judge set aside to wait is missing from the first view. Then it runs `check` over what it holds, creating what
|
||||
is missing. The judge checks every entry once, at the merge check and at take-in (ADR 0578; ADR 0595 §8), and also
|
||||
each ledger type's operations: a set-entry or a remove names `into` and `key`, `fields` and an index's `on` name
|
||||
fields of the schema, and `digest-of` names a text field.
|
||||
- **A module that asks bus state waits for the bus module** (ADR 0614 §5). Each bus-state entry of a manifest is a
|
||||
requirement on the provision `bus-state`, which the bus module's manifest provides; the module graph reads it as any
|
||||
requirement, so the assigner holds a consumer's new build until a build of the bus module that offers `bus-state`
|
||||
runs healthy (ADR 0603 §5). That each entry exists before its user starts is the bus module's own work, above, not
|
||||
an edge.
|
||||
- **The bus module has a process of its own** beside its tool bundle, with a declared health check. Its credential
|
||||
creates streams and buckets under modules' names only, and deletes only through `drop` and `recreate`. Its health
|
||||
turns unhealthy, naming the cause, when an accepted verdict has waited on its durable consumer, unanswered, for
|
||||
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry at 75 percent
|
||||
raises that module's condition and leaves the bus module healthy.
|
||||
- **The binding travels on `created`** (ADR 0595 §1, §7). The grants part, which issues every membership, hears it
|
||||
on a durable consumer and writes the binding (the names on the bus) into the module's membership, kept in its own
|
||||
state `memberships`, so no part writes another's state and a binding outlives the 30 days its event is kept.
|
||||
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry past the raise mark
|
||||
raises that module's condition and leaves the bus module healthy. **Its process has a bus account of its own**,
|
||||
composed by the grants part for the holder of the seat `mesh-broker` (a seat, never a module's name), minted by the
|
||||
vault as an own secret: JetStream create and update under modules' names only (streams, buckets and object stores
|
||||
`<module>_*`), their delete subjects, and the seat's subjects for `create`, the seat's `apply`, `recreate` and
|
||||
`drop`, which no other account holds, the tool runner's included.
|
||||
- **The binding travels on the bus module's events** (ADR 0595 §1, §7, as their progressive insight of 2026-11-03
|
||||
has it, waiting for the operator's Approve). The grants part, which issues every membership, hears them on a durable
|
||||
consumer: it writes the **binding** (the names on the bus) into the module's membership on `created`, on every
|
||||
machine the module is assigned, rewrites it on `recreated` and removes it on `dropped`, kept in its own state
|
||||
`memberships`, so no part writes another's state and a binding outlives the 30 days its event is kept.
|
||||
A module whose binding is missing at start fails loudly, naming the entry; it never starts blind. A `refused`
|
||||
create hands no binding. **A refused or overdue creation is loud**: overdue means no `created` or `refused` within
|
||||
five minutes of an accepted verdict that asks for the entry. The condition is raised by the watchman, which today raises the judge's
|
||||
@@ -710,6 +748,73 @@ raises that module's condition and leaves the bus module healthy.
|
||||
and `refused`; never by the bus module, which cannot raise it while it is down; it names the module
|
||||
and the entry; a later `created` clears it, and so does a later accepted verdict of that module that no longer asks
|
||||
for the entry.
|
||||
- **A foundation part's first view of another's state is a tool its owner answers in a runtime of its own** (ADR 0615,
|
||||
ADR 0621). The four
|
||||
foundation parts that read each other (the grants part, the tool runner, the indexer and the assigner) stop
|
||||
reading the indexer's and the assigner's bus state in the milestone of ADR 0612, with the other 19 readers of that
|
||||
state (ADR 0615 §1). **Each owner serves them as tools of its own module**, the indexer `manifests` and
|
||||
`modules`, the assigner `placement`, `seat-holders` and `wants`, and declares `runs-as`, so its tools bundle runs in
|
||||
a runtime of its own on the module's own bus account, never in the node's tool runner, and the tool runner never
|
||||
asks through itself (ADR 0621 §1). No new manifest form is added: a process still answers no tool call or seat verb
|
||||
(ADR 0436 §3). **Each verb is granted to exactly its readers** (ADR 0621 §2, as ADR 0615 §3 lists them): `placement`
|
||||
to the grants part, the tool runner and the indexer; `seat-holders` to the tool runner; `wants` to the grants part;
|
||||
`manifests` and `modules` to the grants part, the tool runner and the assigner. A reader declares each in `invokes`
|
||||
as `<module>.<tool>` only where it is granted, which is the grant the grants part composes as for any tool, and
|
||||
each tool checks the proved caller against a `callers` list holding exactly its readers and refuses any other, the
|
||||
owner itself included. **The judge of the milestone is its own group's** (ADR 0621 §4): since no field is added,
|
||||
the running judge takes the indexer's and the assigner's manifests in as it does today, and the merge check judges
|
||||
them with the judge built from the group's own head; no release of the judge goes ahead of the milestone. **The
|
||||
owner's runtime answers before any first view of its own** (ADR 0621 §3): at once from its module's own state, as
|
||||
soon as it is connected and before it has asked any other part anything, with the revision of its state it answered at,
|
||||
so the indexer and the assigner, each asking the other at start, never wait on each other (§4). The reader then
|
||||
follows the owner's declared events, each carrying the revision of the state it follows, and never asks the verb
|
||||
again on a timer; a gap in revisions makes it ask once more, said in its log (§5). The owners' events are new
|
||||
families of their own, apart from the indexer's `manifest-refused` and `manifest-accepted` and the assigner's
|
||||
`put`: the indexer's `indexer.views` and the assigner's `assigner.views`, one event per changed key with its
|
||||
bucket, key and revision and the key's value whole, or a delete mark for a key removed, so a reader keeps its
|
||||
picture from the events alone and asks the verb only on a gap (ADR 0615 §5). **Both families are declared in
|
||||
their owners' old `streams` sections**, each an entry with `max-age-days` 7 and `max-mib` 256: the old section keeps
|
||||
a stream by limits, which drops its oldest when full (only a work queue refuses the new instead), so a reader that
|
||||
misses a dropped event sees the gap in revisions and asks once more. The indexer's own kept data stays on the old
|
||||
sections (ADR 0595 §10 names it among the foundation); the assigner's goes beside its existing `streams` entry
|
||||
`placement`, under ADR 0619, which extends ADR 0595 §10's foundation to the parts
|
||||
the bus module's own composition, placement, build or delivery needs before it can answer: the assigner, the tool
|
||||
runner, the builder, the checker, mesh-config and the vault. **The size, an estimate**: read on the bus on
|
||||
2026-11-04, the buckets the readers read hold about 3 MiB of current values at the indexer (its manifests about
|
||||
5 KiB a value) and about 1.5 MiB at the assigner (its wants about 0.25 KiB a value). The milestone's one mesh-sdk
|
||||
rebuild rewrites every module's keys, up to three times in its window (design 51 *How many builds*), so a week of
|
||||
it is some 20 MiB on `indexer.views` and some 10 MiB on `assigner.views` with ordinary merges added, each well
|
||||
under its 256 MiB. Each event carries one value whole, the largest today a manifest of tens of KiB, under the bus's
|
||||
largest message, 1 MiB (the bus server's `max_payload`); an owner whose value with its event would pass it emits
|
||||
the key's revision alone, as a gap, so the reader asks the first view once, and the owner writes its finding
|
||||
naming the key.
|
||||
A waiter on an existing event, `manifest-accepted` or the
|
||||
assigner's `put`, never wakes on them. **When an owner's revisions go back**: should an owner's state be rebuilt
|
||||
by the bus module's `recreate`, its revisions start again; a reader that hears a revision lower than the one it
|
||||
holds takes it as a gap and asks the first view again, and keeps what that answer says, whatever revision it held
|
||||
before. A `recreate` that could not keep the data answers empty at revision 0, so the grants part keeps its list
|
||||
in force (below): a login revoked meanwhile stays until the owner's first change, and the grants part's
|
||||
first-view condition stays raised meanwhile, naming the owner, the empty answer and the operator's step: let the
|
||||
owner write its state again from its sources (the indexer by taking each module in again, `indexer_take_in`; the
|
||||
assigner by its next composition of the wants), then ask the grants part's first view again,
|
||||
`grants_first_view_again`. It is cleared by an answer above revision 0. A reader that starts while its owner is down raises a
|
||||
condition naming the owner and the verb past its bound and keeps asking until it answers, never reading the
|
||||
owner's state instead; once it had its first view, it keeps working on what it heard (§6). The bound is longer
|
||||
than an owner's start (two minutes), so a normal start, readers and owners coming up together, notifies nobody;
|
||||
the owner's answer clears the condition. An answer that is empty at revision 0 (an owner that holds nothing yet)
|
||||
is a first view of nothing, never a reason for the grants part to drop a login it holds: it keeps the list in
|
||||
force until an answer at a revision above 0. Each reader's tools say the owner, verb and revision its first view
|
||||
holds, when it asked and when it was answered (a read tool), and ask the first view again by hand (a re-read tool,
|
||||
recorded as a hand act): the grants part's `grants_first_views` and `grants_first_view_again`, the tool runner's
|
||||
`mesh_first_views` and `mesh_first_view_again`, the indexer's `indexer_first_views` and
|
||||
`indexer_first_view_again`, and the assigner's `assigner_first_views` and `assigner_first_view_again`. The tool
|
||||
runner's two are tools of the mesh MCP server, beside `mesh_runtimes`, since the tool runner serves its own state
|
||||
there: the mesh MCP server gains them, and the operator sees them among its tools. **On a fresh mesh** the
|
||||
assigner starts before any new-form bus state exists and needs no binding of the bus module first: its own state
|
||||
and `assigner.views` are on its old sections, which the grants part creates, so it answers the indexer, the grants
|
||||
part and the tool runner their first views while the bus module still waits on it. The order of owner before reader is kept by the module graph's served-verb edge for the 19 readers (ADR 0614 §2), and,
|
||||
for the four, whose verb edges lie on cycles the graph leaves out (ADR 0614 §3), by the owner's answer at once and
|
||||
the reader's wait (ADR 0615 §2).
|
||||
- **Only the owner reaches it** (ADR 0596 §2). The membership grants the owner's account its own entries and no
|
||||
other module's: the new form has no readers. What another module needs of that data it asks through the owner's
|
||||
tools or seat verbs, and what the owner lets others hear is its declared events, subscribed to through
|
||||
@@ -717,9 +822,11 @@ raises that module's condition and leaves the bus module healthy.
|
||||
|
||||
### 12.3 The seat's verbs, events and fill
|
||||
|
||||
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). Where the seat's definition
|
||||
lives, and so the exact form of each verb's arguments, waits for the research effort on how seats are defined,
|
||||
tracked on issue 1966; the build's third step does not start before it.
|
||||
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). It is one of the nine seats
|
||||
defined whole in the milestone of ADR 0612, its contract in `mesh-contracts` (ADR 0613 §1; design 26 *A seat's
|
||||
definition*), which settles where its definition lives (ADR 0595 §12). An entry's arguments are the module, its local
|
||||
name, its storage type, its template, its options and its configuration; what is held answers the settings held and
|
||||
the fill (count, bytes, the cap); `drop` and `recreate` carry the warrant and why.
|
||||
|
||||
| Verb | Does | Who may call it |
|
||||
|---|---|---|
|
||||
@@ -739,7 +846,8 @@ the bus module takes a stream snapshot of the entry. A drifted entry is said, ne
|
||||
that grows is applied without a person.
|
||||
|
||||
**Events**: `created`, `refused` (with why), `drifted` and `drift-cleared`, `full` and `full-cleared`, `orphaned`
|
||||
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it);
|
||||
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it), `recreated` and
|
||||
`dropped` (ADR 0595 §6, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve);
|
||||
each carries the module, the local name, the bus name, the template, and the settings asked against those held. The
|
||||
bus module's own kept data stays on the old sections (ADR 0595 §10), because it cannot wait for its own process to
|
||||
create it: it declares this stream in its `streams` section, kept 30 days (ADR 0363's bound), created by the grants
|
||||
@@ -749,22 +857,31 @@ live state. The tool `history` of one entry reads them back: every event the bus
|
||||
settles where they are kept, which ADR 0595 §12 left to the design.
|
||||
|
||||
**Fill is the bus module's one periodic read** (ADR 0595 §6): every minute, for every entry that refuses when full,
|
||||
because the bus says nothing as a store fills. The condition `full` is raised from 75 percent of the cap and cleared
|
||||
below it. The grants part's `mesh-grants.history-full` moves to it: the new condition is raised first, then the old
|
||||
key cleared. A bare stream that refuses when full is read the same way. **The conditions**, each naming the module
|
||||
and the entry: `full` (cleared below 75 percent), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
|
||||
because the bus says nothing as a store fills. The condition `full` is raised at a raise mark and cleared below a
|
||||
lower clear mark, so an entry sitting at the mark does not flap; the marks are settings of the bus module, by default
|
||||
a raise at 75 percent of the cap and a clear below 70 (ADR 0595 §6, as its progressive insight of 2026-11-03 has it).
|
||||
The grants part's `mesh-grants.history-full` moves to it: the new condition is raised first, then the old key cleared;
|
||||
no manifest on main asks for a history stream or objects today, so the move loses nothing. A bare stream that refuses
|
||||
when full is read the same way. **The conditions**, each naming the module and the entry: `full` (cleared below the
|
||||
clear mark), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
|
||||
`drop` or by a manifest asking for it again) and a refused create (cleared by a later `created`).
|
||||
|
||||
**Its tools** (tools-are-how-a-module-is-spoken-to): the four reading verbs, marked reads, answer every entry the
|
||||
bus module created and its fill, and `history` its events for one entry; `create` and `apply`, marked acts, and
|
||||
`recreate` and `drop`, marked dangerous and so granted only by name (ADR 0386 §6, ADR 0387), are its tools for a
|
||||
person. Such a tool, in the bus module's bundle, holds no grant of those verbs' subjects, and neither does the tool
|
||||
runner's account: it hands the request to the process on the process's own tool subject, carrying the caller as
|
||||
the token the identity seat signed (ADR 0386 §8), whose signature the process checks, so no account granted that
|
||||
subject can name another caller; the process acts only for a caller whose role grants that tool, for `recreate` and `drop` only once
|
||||
the operator's warrant is held, and answers what it did. The bus seat's `apply` (a difference that grows) and the
|
||||
runner's account: it hands the request to the process on the process's own tool subject. The caller travels as the
|
||||
token the identity seat signs (ADR 0386 §8), whose signature the process checks, so no account granted that subject
|
||||
can name another caller. **Until that token exists** (design 58 builds the identity seat), the process's tool subject
|
||||
may be published on by tool runners' accounts alone, and the process trusts the caller the tool runner proved and
|
||||
stamped (ADR 0386 §7), never a name in the request's body; `recreate` and `drop` act only on the operator's warrant,
|
||||
which is the guard that holds either way. The process acts only for a caller whose role grants that tool, for
|
||||
`recreate` and `drop` only once the operator's warrant is held, records each as a hand act (`mesh-hand-acts`), and
|
||||
answers what it did. **The mesh deletes kept data only through `drop` and `recreate`** (ADR 0595 §4, as its
|
||||
progressive insight of 2026-11-03 has it): the bus module's older remove tools (its stream, bucket and consumer
|
||||
removals, ADR 0564) refuse an entry of the new form, naming `drop`'s tool, and keep working for the old sections. The bus seat's `apply` (a difference that grows) and the
|
||||
ledger's `apply` (§ 12.4) are two verbs on two subjects, granted apart; this design writes them qualified. Its health
|
||||
names the process's last pass, the entries it could not create, and the entries over 75 percent.
|
||||
names the process's last pass, the entries it could not create, and the entries past the raise mark.
|
||||
|
||||
### 12.4 The ledger
|
||||
|
||||
@@ -806,9 +923,12 @@ key-value buckets, and asked for with the template ledger.
|
||||
- **Every instance has a snapshot** (§8): the latest state on a subject of its own, never expiring, written by every
|
||||
`apply`. It makes a kept ledger's rebuild fast and an expiring ledger's possible; a repair that finds it behind
|
||||
the state rewrites it.
|
||||
- **Only the tool runner's ledger credential publishes on a ledger's history** (§9): one per module, held by the
|
||||
tool runner and never by the module's own code, whose account may read its ledger and not publish on it. The grants
|
||||
part issues it with the module's membership on that machine, scoped to the subjects of that module's ledger
|
||||
- **Only the tool runner's ledger credential publishes on a ledger's history** (§9): one per module assignment, that
|
||||
is per node the module runs on, the account `ledger.<node>.<module>` in the tool runner's own secret `ledgers` on
|
||||
that node (ADR 0597 §9, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve), held by
|
||||
the tool runner and never by the module's own code, whose account may read its ledger and not publish on it. A
|
||||
person's `rotate` of that secret is refused by the vault, as a bus account's is. The grants part issues it with the
|
||||
module's membership on that machine, scoped to the subjects of that module's ledger
|
||||
histories and snapshots, and removes it when the module is unassigned from the machine. Only the
|
||||
owner's account reads the history, the snapshot and the state.
|
||||
- **An index** is a second key-value bucket kept by a watcher in the owning module's process, rebuildable from the
|
||||
@@ -843,13 +963,20 @@ and the new form are never both used for one local name. During the move the gra
|
||||
sections' entries, so each entry has one creator. The indexer counts the modules on each old section, a tool reads
|
||||
the count, and the judge refuses a section once its count is 0. The foundation's own kept data (the bus module's,
|
||||
the grants part's and the indexer's) stays on the old sections for good and is not counted: those modules cannot
|
||||
wait for a process that needs them first.
|
||||
wait for a process that needs them first. ADR 0619, extending ADR 0595 §10's foundation, adds the parts the bus module's own composition, placement, build or delivery needs before it can answer:
|
||||
the assigner, the tool runner, the builder, the checker, mesh-config and the vault; their new state and streams of
|
||||
this milestone go on their old sections too (§ 12.2; design 51 *The move*; design 32).
|
||||
|
||||
**Readers move after their owners** (ADR 0596 §3). Today 195 `reads` entries in 37 modules read another module's
|
||||
bus state, the indexer's 36 times and the assigner's 30. An owner declares the events and gains the tools its
|
||||
readers need before any reader moves: the judge refuses a moved module's subscription to an event its owner does
|
||||
not declare, and the review of a reader's move reads that the owner's tool is on main and delivered. These moves
|
||||
are tracked by issue 1966.
|
||||
bus state (counted 2026-11-03 02:00). **A correction of fact**: the indexer's and the assigner's state, counted again
|
||||
from every manifest on main later that day, is read by 23 modules, 9 buckets of the indexer and 6 of the assigner,
|
||||
not by the 19 the first plan counted; and 151 state entries were counted, not 158 (the template table of § 12.1 keeps
|
||||
the first count, which chose the templates). The owners first gain their events and first-view tools: the indexer
|
||||
one per bucket its readers read, the assigner the same. Then all 23 readers move in the milestone of ADR 0612, the
|
||||
four foundation readers included, by the first view of § 12.2 (ADR 0615 §1); their order is the module graph's, not
|
||||
a review reading an owner delivered (ADR 0615 §2). The judge still refuses a moved module's subscription to an event
|
||||
its owner does not declare. Every other reader moves when its module is next touched. These moves are tracked by
|
||||
issue 1966.
|
||||
|
||||
### 12.7 How it is checked
|
||||
|
||||
@@ -857,7 +984,16 @@ are tracked by issue 1966.
|
||||
|---|---|
|
||||
| A manifest's bus state is valid, once | the judge's unit tests, each refusal naming the field: an unknown template, an option outside its bound, past values 0, "when full" given, a fixed setting given, a name used twice across storage types, two modules' bus names alike, a `reads` in the new form, an effect outside the fixed set (ADR 0595 §8, ADR 0596 §2, ADR 0597 §2) |
|
||||
| The bus module creates, and the binding follows | live, by tool call: a module asking for a state, a log, objects and a ledger in `requires` receives each binding in its membership, written after `created`, never before; `show` gives each entry's template, settings and fill; a module calling `create` is refused |
|
||||
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; `recreate` and `drop` without the operator's warrant are refused, and with it act after a stream snapshot; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at 75 percent and cleared below |
|
||||
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; `recreate` and `drop` without the operator's warrant are refused, and with it act after a stream snapshot; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at the raise mark and cleared only below the clear mark; `recreated` rewrites the binding and `dropped` removes it; at start the first view from `indexer_bus_state` creates what a verdict given while it was down asked; the older remove tools refuse a new-form entry, naming `drop`'s tool |
|
||||
| A module asking bus state waits for the bus module | the module graph's table test: a manifest with a ledger entry has a requirement edge to the module providing `bus-state`, one without has none (ADR 0614 §5) |
|
||||
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`cmd/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`cmd/mesh-delivery-assigner`) and the grants part's (`cmd/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the
|
||||
| The owners' first views are their own tools, in a runtime of their own (ADR 0621) | cases of the indexer's and the assigner's whole-module integration tests: each first-view tool, called from a reader's account while the node's tool runner is stopped, answers (§1); each answers its granted readers and refuses every other account, the owner's own included (§2); the two runtimes started at the same moment, each asking the other's first view at start, both answer within the first-view bound with their revisions (§3); and the milestone's group passes its merge check with no change to the judge's known fields for these verbs (§4) |
|
||||
assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus
|
||||
module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's count of `reads` entries on the indexer's and the assigner's state reads 0 |
|
||||
| A foundation part's first view comes within its bound | live, after the delivery: each reader's read tool (`grants_first_views`, the mesh MCP server's `mesh_first_views`, `indexer_first_views`, `assigner_first_views`) shows, for the indexer's and the assigner's verbs, the time from its ask to the owner's answer under the two-minute bound, and no first-view condition raised by the delivery |
|
||||
| The bus module's first view | cases in the nats module's one integration test: a replayed verdict older than the first view is not applied; an `indexer_bus_state` that does not answer turns the health unhealthy naming it and is asked again on the backoff; a set-aside manifest missing from the first view keeps its `asked` key, an empty `manifest-accepted` removes it; a deleted module's entry, after its empty `manifest-accepted`, is listed by `orphans` and raises `orphaned`, and a set-aside module's entry is neither; `nats_first_view_again` asks at once and records the hand act |
|
||||
| `manifest-accepted` is never lost silently | unit tests of the indexer: a failed publish is published again, and one still failing writes the finding; `indexer_say_accepted` says it again for one module from the verdict held, and refuses a module with no accepted verdict |
|
||||
| Until the identity seat, the caller is the one the tool runner proved | a unit test of the bus module's process: a request on its tool subject naming another caller in its body acts for the proved caller; `drop` without the warrant is refused |
|
||||
| Drift and orphans, live | live, made only by a manifest change: a manifest that shrinks a setting raises `drifted`; the conditions clear as above. Nothing is filled on the live bus, and no orphan is made there |
|
||||
| Overdue and stopped are loud | live, carried by the change of the first module that moves in step 5, never by a merge made for the test alone: with the bus module's process stopped, an accepted manifest asking for an entry raises the overdue condition within five minutes, from the watchman, and a later `created` clears it; the bus module's health turns unhealthy when its pass is more than three minutes old |
|
||||
| A person's tools reach the process, and only by role | live: a role without the dangerous grant is refused `drop`'s tool; the tool runner's account holds no grant of `create`, the seat's `apply`, `recreate` or `drop` |
|
||||
@@ -895,7 +1031,9 @@ ruled on every finding. What was found, and what answers it:
|
||||
| upgrade | the build order lacked the grants part's binding and two undo steps | design 25 § 12.6 |
|
||||
| tools | the ledger's repairs had no tool for the next owner; the bus module's events had no read tool | design 25 § 12.5 (served for every ledger owner), § 12.3 |
|
||||
|
||||
**Left to the operator**, because each changes an accepted record: a margin between raising and clearing the
|
||||
condition `full` (ADR 0595 §6 raises and clears it at 75 percent, so an entry sitting there flaps), and an event of
|
||||
its own for `drop` and `recreate`. **A correction of fact** for ADR 0595 §4: the bus module deletes through `recreate`
|
||||
as well as `drop`. Each is noted on issue 1966.
|
||||
**Answered since**: the margin of `full` and the events of `drop` and `recreate` were the operator's answers of
|
||||
2026-11-03 and are now ADR 0595 §6's progressive insight, with the correction of fact for ADR 0595 §4 (the bus
|
||||
module deletes through `recreate` as well as `drop`); both wait for the operator's Approve and are written in § 12.2
|
||||
and § 12.3. The milestone's amendment (ADR 0613 to ADR 0615: the first view, the bus-state edge, the deletion paths,
|
||||
the caller until the identity seat, the reader count) awaits its own review by architectural domain, which rewrites
|
||||
this section.
|
||||
|
||||
@@ -15,9 +15,13 @@ code:
|
||||
- mesh-catalog modules/gitea/module.json
|
||||
- mesh-controller internal/inventory/migrations/0063-a-machines-names-are-one-seats.sql
|
||||
- mesh-controller internal/inventory/migrations/0064-the-resolver-file-is-the-uplinks.sql
|
||||
updated: 2026-11-03
|
||||
updated: 2026-11-04
|
||||
decisions:
|
||||
- "ADR 0608"
|
||||
- "ADR 0613"
|
||||
- "ADR 0620"
|
||||
- "ADR 0612"
|
||||
- "ADR 0614"
|
||||
- "ADR 0605"
|
||||
- "ADR 0287"
|
||||
- "ADR 0223"
|
||||
@@ -141,7 +145,7 @@ convention, which later seats departed from.
|
||||
|---|---|---|---|
|
||||
| `mesh-controller` | — | mesh | — | the controller |
|
||||
| `mesh-store` | — | mesh | — | the store the mesh's own records live in |
|
||||
| `mesh-broker` | — | mesh | `mesh-bus` | the broker carrying the mesh's own bus |
|
||||
| `mesh-broker` | — | mesh | `mesh-bus`, and `bus-state` (ADR 0595 §1, ADR 0614 §5) | the broker carrying the mesh's own bus; its holder, the bus module, creates the bus state modules ask for (design 25 § 12) |
|
||||
| `mesh-vault` | — | mesh | `secret` | the vault (in the seed since ADR 0161; the earlier *reserved* named an effect no rule produced) |
|
||||
| `container-image-registry` | `mesh-artifact-store` for images (ADR 0605; the old name readable until every node-engine reads the new one) | mesh | `artifact-store` | the artifact registry (`distribution`) |
|
||||
| `oci-artifact-registry` | `mesh-artifact-store` for bundles and archives (ADR 0605) | mesh | `artifact-store` | the artifact registry (`distribution`) |
|
||||
@@ -172,44 +176,114 @@ merges.
|
||||
## A seat's definition (from 2026-11-03)
|
||||
|
||||
**A seat is defined once, in one language-neutral file, and every part learns it from the bus** (ADR 0608, from
|
||||
research 081). The registry seats (design 51) are the first defined this way, then `mesh-broker`'s verbs.
|
||||
research 081). The registry seats (design 51), `mesh-broker` (design 25 § 12.3) and the four seats the milestone
|
||||
adds verbs or events to are the first defined whole this way, in the milestone of ADR 0612 (ADR 0613 §1,
|
||||
ADR 0620).
|
||||
|
||||
- **The file** is a schema with conformance fixtures: the seat's verbs and their messages, its events and their
|
||||
bodies, its state and the forms of its provisions. It lives in the contracts module of its area. Its Go and
|
||||
TypeScript types are generated from it and committed beside it; nobody writes them by hand.
|
||||
TypeScript types are generated from it and committed beside it; nobody writes them by hand. The format is JSON, its
|
||||
messages and bodies JSON Schema, as the seat set's verb inputs already are, with the format's own schema checked in;
|
||||
the fixtures are data, never functions.
|
||||
- **What is defined whole now** (ADR 0613; ADR 0620, on the
|
||||
operator's answer of 2026-11-04 to move them all). Every seat this milestone changes is defined whole and its
|
||||
contract moves into `mesh-contracts`: `npm-package-registry`, `go-package-registry`, `container-image-registry`,
|
||||
`oci-artifact-registry` (one common part of their shared verbs, written once, so the four cannot drift) and
|
||||
`mesh-broker`; and the four seats it adds a verb or an event to, each with every verb its holder's manifest on the
|
||||
trunk declares today, plus the new ones. **No event moves subject in this milestone** (ADR 0620): a seat's own events are only those published on its seat subject today,
|
||||
and a holder's own events stay under the holder's own subjects, listed beside the seat they belong with, never moved
|
||||
onto it:
|
||||
- `mesh-delivery-checker` (held by the checker): the verbs `try`, `try-read` and `try-end`; no seat event. The
|
||||
holder's own, beside it: its family `checker.asks`, new, with `tag-wanted` (design 51 *Tagged versions*) and
|
||||
`verdict` (*Out of step is learned from events*, below); and its stream `checks`, which already declares
|
||||
`preview.**` and `owed.**`, only its `emits` lacking `preview.written`, which the same change adds.
|
||||
- `mesh-config` (held by mesh-config): the verbs `config` and, new, `renames`; no seat event. The holder's own,
|
||||
beside it: `config-changed`, and its family `config.events`, new, with `renamed`, `retiring` and `retired`
|
||||
(design 32).
|
||||
- `mesh-vault` (held by the vault): the verbs `get`, `secret-ask`, `issued`, `rotate`, `move-in`, `node-key`,
|
||||
`own`, `issue-account`, `sign`, `enrolment-token`, `node-account`, `rotate-node`, `node-account-confirm`,
|
||||
`abandon` and, new, `retire`; no seat event. The holder's own, beside it: `provisioned`, `rotated`,
|
||||
`secret-changed`, `account-hashed`, `own-replaced`, `account-issued`, `signed`, `rotation-begun`,
|
||||
`node-account-ready`, `rotation-done` and, new, `own-retired` (design 32). The seat list mesh-sdk compiles
|
||||
today also names `accept` and `move-asks`, which no holder serves and nothing calls: the same mesh-sdk change
|
||||
drops them from the seat, and its `vault_seat_test.go` with them.
|
||||
- `operator-channel` (held by the router, the messenger module): the verbs `open`, `history`, `notify`, `tell`
|
||||
and `asks`; the asks it accepts, `ask`, `cancel` and `condition`; its seat event `decided`; `by-caller` for
|
||||
`ask`, `cancel`, `condition` and `decided`; and `records` `asks`, as the holder's seat entry has them. The
|
||||
holder's own, beside it: `refused`, and `offered` and `closed`, new, on its restricted stream (design 55 § 11).
|
||||
mesh-sdk's seat list holds no entry for it today, so its definition is written from the holder's seat entry.
|
||||
|
||||
**How it is checked**: each of these definitions' conformance fixtures names every event's subject, the seat's and
|
||||
the holder's own, and a case of the holder's whole-module test takes a fixture from the running holder (an event
|
||||
it publishes) and matches it, subject and body, against the definition. The builder and the indexer hold no seat:
|
||||
the builder's `numbered` and `old-form-closed`, the indexer's `manifest-accepted` and its family `indexer.views`
|
||||
are those modules' own, declared in their own manifests, which
|
||||
ADR 0613 does not govern, and no seat definition carries them. Every other seat, the foundation's included, has its definition on the bus in the same milestone, all
|
||||
at once (ADR 0608 §2), holding the seat list's grain: its name, whether it is held once or on every node, its verbs
|
||||
with their inputs and its provision, generated from the seat list mesh-sdk compiles today, so the move changes
|
||||
nothing a reader sees (ADR 0613 §2). Its messages, events and state layouts are added to
|
||||
a seat's definition when its area's contract next changes, which moves that area's seats whole into its contracts
|
||||
module in that change (ADR 0613 §3); which area moves when is tracked by issue 167 (ADR 0613 §4).
|
||||
- **The seat set has one source.** The judge, the node-engine, the tool runner and claude-code each compile a copy of
|
||||
the seat list today; all four import the set generated in `mesh-contracts` instead. The tool runner's descriptions
|
||||
and claude-code keep that compiled set, equal to the trunk's definition, in this milestone, so neither depends on
|
||||
the indexer at its start; they move to `indexer_seat_definition` and its event when their next change moves them,
|
||||
tracked on issue 1966.
|
||||
- **The generator** is each language's build module's: the Go build module's package for Go, the Node and TypeScript
|
||||
build module's for TypeScript (the logic is the module's whose work it is). The check of a change regenerates and
|
||||
fails, naming the file, when a committed type differs; its verdict event names the out-of-step files, and that is
|
||||
how the indexer learns of them. The indexer runs no generator.
|
||||
build module's for TypeScript (the logic is the module's whose work it is). The generator's version is a build input
|
||||
of every contracts module, so a changed generator rebuilds each contracts module once (ADR 0603 §3). Each build
|
||||
module's test step regenerates and compares, for every module carrying the contracts marker (below): a committed
|
||||
file that differs, a file missing, or a file in the package tree the generator did not write fails the step, naming
|
||||
each path.
|
||||
- **Out of step is learned from events, and the indexer runs no generator.** For a pull request, the check's step
|
||||
fails with the out-of-step paths; the checker puts them in its verdict and emits its declared event `verdict`
|
||||
carrying them (its `preview.written` is declared in the same change). For a trunk, the builder's build-end event
|
||||
carries a trunk build's out-of-step paths, which is how a generator's change is seen, since no check of the
|
||||
contracts module runs then. The indexer consumes both and writes the trunk's finding.
|
||||
- **On the bus**: the indexer reads every seat definition from every trunk and open pull request, from the forge
|
||||
bridge's messages, and keeps them in its own state `seat-definitions`, keyed by seat and source (the trunk, or a
|
||||
pull request's head), as their only writer; a definition is at most 256 KiB. It emits `seat-definition-changed`
|
||||
(seat, source, digest), kept seven days. **Only the trunk's entry is the seat's contract**: the readers (the contracts
|
||||
modules' builds, the tool runner's descriptions, the review) ask `indexer_seat_definition`, which answers the trunk's
|
||||
unless a pull request's head is named, and says which it answered. A pull request's entries are removed on the forge
|
||||
bridge's event that it closed or merged. A definition that does not parse fails its pull request's check; the
|
||||
trunk's stays. The merge is the only approval. The foundation's own definitions are bootstrapped before the bus
|
||||
exists, as the bootstrap of a fresh mesh has it (research 075).
|
||||
(seat, source, digest), kept seven days, only when a digest changed. **Only the trunk's entry is the seat's
|
||||
contract**: the readers (the contracts modules' builds, the review, and later the tool runner's descriptions) ask
|
||||
`indexer_seat_definition`, which answers the trunk's unless a pull request's head is named, and says which it
|
||||
answered. A pull request's entries are removed on the forge bridge's event that it closed or merged. A definition
|
||||
that does not parse fails its pull request's check; the trunk's stays. The merge is the only approval. The
|
||||
foundation's own definitions are bootstrapped before the bus exists, as the bootstrap of a fresh mesh has it
|
||||
(research 075).
|
||||
- **Findings**, written by the indexer, from which the condition keeper raises and clears the conditions (ADR 0564): a
|
||||
trunk's seat definition that does not parse (a pull request's is its check's verdict, not a condition); generated
|
||||
types on a trunk out of step with their definition. **Tools** of the indexer: `indexer_seat_definition` (read one),
|
||||
`indexer_seat_definitions_again` (read a trunk's or a pull request's again now) and `indexer_out_of_step` (the
|
||||
generated types out of step).
|
||||
trunk's seat definition that does not parse, `seat-definition-unparsed` (a pull request's is its check's verdict, not
|
||||
a condition), cleared by a definition that parses; generated types on a trunk out of step with their definition,
|
||||
`generated-types-out-of-step`, cleared only by a clean build end of the same contracts module. **Tools** of the indexer: `indexer_seat_definition`
|
||||
(read one), `indexer_seat_definitions_again` (read a trunk's or a pull request's again now) and `indexer_out_of_step`
|
||||
(the generated types out of step).
|
||||
- **Contracts modules.** A seat's contract is published by a contracts module, which holds contracts only (every file
|
||||
generated from a seat definition) and no seat; the seat's holder implements it. The seat contracts mesh-sdk holds
|
||||
move into `mesh-contracts` in one change, delivered with the first numbered publish (design 51, *The move*, step 2);
|
||||
an area's contracts move into `mesh-<area>-contracts` when the area's contract next changes, and that change creates
|
||||
the module. mesh-sdk keeps its name and the shared runtime. A catalog check refuses a contracts module holding a file
|
||||
its generator did not write, and a seat-specific type in mesh-sdk once `mesh-contracts` publishes.
|
||||
generated from a seat definition) and no seat; the seat's holder implements it. **The contracts marker**: a contracts
|
||||
module's package artifact names the directory of its definitions in the field `generated-from`, and the judge
|
||||
refuses a module carrying it that claims a seat, naming the seat. `mesh-contracts` declares two packages, Go and
|
||||
TypeScript, in package series `0.1`, published through the two package seats (design 51) and taken by its users by
|
||||
version, or inside one delivery group by the group's head. No `mesh-<area>-contracts` is created in this milestone:
|
||||
an area's contracts module is created by the change that next moves its area. mesh-sdk keeps its name and the
|
||||
shared runtime, and takes no new seat-specific type (ADR 0613 §5): a unit test of mesh-sdk refuses an exported
|
||||
identifier or string constant naming a seat the definitions define. The two checks every change already runs, the
|
||||
build modules' compare step and that test, are where a stray file and a stray type are refused; mesh-catalog's own
|
||||
checks cannot see another repository.
|
||||
- **A contracts cycle holds nothing.** `mesh-contracts` is published through the forge module's package seats and
|
||||
built by the Go and Node build modules, and the forge module, `distribution` and the Go build module import it. A
|
||||
package edge and a build module's edge are triggers, never waits, so that cycle starts and is not refused (ADR 0614
|
||||
§6), and a package is numbered per source revision, so the triggers end (design 51 *The versions*).
|
||||
- **Older readers keep working**: a moved contract keeps its subjects, keys and message shapes, and a reader ignores
|
||||
a field it does not know.
|
||||
- **A vendor-specific** provision or contribution lives in its module's own extension package, published by version
|
||||
like any package; a later holder of the seat may publish its own the same way. A seat's definition stays
|
||||
vendor-neutral, which the seams review checks.
|
||||
|
||||
Acceptance tests: research 081's 14, 21, 25 and 26, and cases of the indexer's whole-module integration test: a trunk
|
||||
definition that does not parse writes its finding and a fixed one clears it; a check verdict naming out-of-step types
|
||||
writes the finding and a clean one clears it; `indexer_seat_definition` answers the trunk's entry while a pull request
|
||||
Acceptance tests: research 081's 14, 21, 25 and 26; a unit test that the generated seat set equals the seat list
|
||||
mesh-sdk compiled before the move, entry for entry; mesh-sdk's seat-naming test; each build module's compare step
|
||||
failing on a differing, a missing and an unwritten file, naming each; the checker's `verdict` naming out-of-step
|
||||
files; and cases of the indexer's whole-module integration test: a trunk
|
||||
definition that does not parse writes its finding and a fixed one clears it; a trunk build end naming out-of-step types
|
||||
writes the finding, a clean build end of another contracts module leaves it, and a clean one of the same module clears
|
||||
it; `indexer_seat_definition` answers the trunk's entry while a pull request
|
||||
holds another; a pull request's entries go when it closes; a definition over 256 KiB is refused; each of the three
|
||||
tools answers.
|
||||
|
||||
@@ -403,3 +477,7 @@ and 32 by the four reviewers by architectural domain; the findings and what answ
|
||||
*Reviewed by architectural domain*. For this design: only the trunk's definition is the contract (security), the
|
||||
indexer learns out-of-step types from the check's verdict event and runs no generator (seams), findings go to the
|
||||
condition keeper (health), a pull request's entries are removed when it closes (running qualities).
|
||||
|
||||
The milestone's amendment (ADR 0612, ADR 0613, ADR 0614: nine seats defined whole, the rest at the seat list's grain,
|
||||
the contracts marker, out of step learned from the checker's `verdict` and the builder's build end, the seat set's one
|
||||
source) awaits its own review by architectural domain, which rewrites this section.
|
||||
|
||||
@@ -13,9 +13,11 @@ code:
|
||||
- mesh-catalog modules/mesh-catalog
|
||||
- mesh-tools node-tools/internal/runtime (a module's state, ADR 0201)
|
||||
- mesh-sdk (the typed bus-state entry in `requires` and its binding, ADR 0595)
|
||||
updated: 2026-11-03
|
||||
updated: 2026-11-04
|
||||
decisions:
|
||||
- "ADR 0609"
|
||||
- "ADR 0612"
|
||||
- "ADR 0613"
|
||||
- "ADR 0606"
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
@@ -491,8 +493,15 @@ complete, because a team that believes it is complete stops reviewing for the ca
|
||||
- **A user outside the package's delivery group is not built before the merge**: it is built after it, when the new
|
||||
version is in its series. A user that breaks fails its own build, is not wanted, keeps its previous build, and a
|
||||
condition tells the operator. A group's members are built in the group's check, against the group's tagged version.
|
||||
- **A contracts module's package** names the directory of the seat definitions it is generated from in the field
|
||||
`generated-from`, the contracts marker (design 26).
|
||||
- **The judge reads the new words** (ADR 0578 §1): each package series well formed and only on the four kinds that may
|
||||
carry it, a package naming `go` or `typescript`, and each name in `packages` a package some manifest declares. A
|
||||
running judge that does not know a word sets the manifest aside to wait, never refuses it, while a newer indexer
|
||||
build is merged and not yet delivered; and a node-engine is handed a manifest using these words only once it
|
||||
states the capability to read them (ADR 0614 §4).
|
||||
- **Cost**: the new manifest fields are read through mesh-sdk's manifest types, so their change rebuilds every module
|
||||
once; it is delivered with design 51's step 2, the one rebuild that step already takes.
|
||||
once; it is the one rebuild the milestone of ADR 0612 already takes for the contracts move (ADR 0612 §1, ADR 0613 §1).
|
||||
|
||||
### A config value renamed (from 2026-11-03)
|
||||
|
||||
@@ -503,25 +512,69 @@ value it holds under the old name to the new name in its own state, without the
|
||||
- **Which values**: a module's own config values only, never a secret, never another module's value. A rename keeps
|
||||
the value's trust: a value set without the operator's Approve stays untrusted under its new name (ADR 0348 §8), and
|
||||
the checker refuses a rename that would change a value's trust.
|
||||
- **How long the old name is read**: until every node-engine whose node has a want of the module reports applied a
|
||||
build of it that reads the new name: mesh-config hears the status view's `delivered` events, and asks its
|
||||
`node_haves` at its start (design 47); until then mesh-config answers both names. A later change may drop
|
||||
`renamed-from` only after that, and the checker refuses it before. The checker also refuses `renamed-from` in any
|
||||
manifest until a build of mesh-config that understands it is delivered, which it learns from the status view's
|
||||
`delivered` events for mesh-config. `config_renames` names, per rename, the nodes still holding the old name open.
|
||||
- **How long the old name is read** (the **rename window**): until every node-engine whose node has a want of the
|
||||
module reports applied a build of it that reads the new name. mesh-config hears the status view's `delivered`
|
||||
events and, at its start and on each, asks its `node_haves` for every node, away ones included, since `delivered`
|
||||
counts only the nodes present (design 47); until then mesh-config answers both names. A node away when the last
|
||||
`delivered` was said should close its window when it returns, with no other delivery: what wakes mesh-config then
|
||||
is to be decided in ADR 0618 (reserved), the node-engine seat's event for a node's stated manifest-reads, said also
|
||||
on its return, as for the old seat name (design 51), never a watch of the node-engine's have state (ADR 0596).
|
||||
**Until ADR 0618 is accepted**, such a node closes the window on the next `delivered`, whichever delivery says it. A node holding a window open
|
||||
for seven days is mesh-config's finding naming the node, cleared when the window closes; `config_rename_close`
|
||||
drops a node from a window by hand, recorded as a hand act.
|
||||
- **The checker asks the seat, never the module.** The seat `mesh-config` gains the optional verb `renames` (the
|
||||
open renames, and the nodes holding each window open), in its seat definition (design 26). The checker asks it for
|
||||
every change carrying `renamed-from` or dropping one: a holder that does not serve it is refused by the tool runner
|
||||
naming the verb, and the checker then refuses `renamed-from` naming that; while a window is open, the same answer
|
||||
refuses dropping `renamed-from`, naming the nodes. A check that cannot ask fails, and says so. So no manifest uses
|
||||
`renamed-from` in the milestone that brings it, whose running mesh-config serves no `renames`; the first that does
|
||||
is its live case. The checker declares `seat:mesh-config.renames` in its `invokes`, so by the served-verb edge
|
||||
(ADR 0614 §2) its new build waits until a build of mesh-config that serves `renames` runs healthy; no cycle closes,
|
||||
since mesh-config waits only on the indexer's `indexer_config_readers`. The "not served" answer is then met only by
|
||||
the running checker in the milestone's own check, and after a revert. `config_renames` names, per rename, the nodes
|
||||
still holding the old name open.
|
||||
- **A refused move** (the old name holds nothing, or a secret) is mesh-config's finding, from which the condition keeper
|
||||
raises the condition. **Tools of mesh-config**: `config_renames` (what moved, from which name, when) and
|
||||
`config_rename_again` (move one value by hand when a move was refused).
|
||||
raises the condition, cleared once the value moved or `renamed-from` left the trunk's manifest. **Tools of
|
||||
mesh-config**: `config_renames` (what moved, from which name, when), `config_rename_again` (move one value by hand
|
||||
when a move was refused) and `config_rename_close` (above).
|
||||
|
||||
### A value no longer read (from 2026-11-03)
|
||||
|
||||
A config value or secret no manifest on any trunk reads any more (mesh-config learns it from the indexer's
|
||||
`indexer_config_readers`, on each of the indexer's events that a manifest was taken in) is marked retiring only once no
|
||||
node-engine reports applied a build that reads it, as the rename's window is (§ above). mesh-config then emits
|
||||
`retiring` (family `config.events`, kept seven days) and asks the operator once, through the operator channel, whether
|
||||
to remove it, naming any node that still read it until lately; it is removed only on the operator's Approve. An ask
|
||||
unanswered for seven days is asked once more, and then written as a finding. `config_retiring` lists what is retiring,
|
||||
since when, and its ask.
|
||||
A config value or secret no manifest on any trunk reads any more is a **retiring value**. mesh-config learns it from
|
||||
the indexer's tool `indexer_config_readers`, which it asks on each of the indexer's `manifest-accepted` events; the
|
||||
indexer says `manifest-accepted` for every newly accepted manifest, its bus-state entries empty when it asks none (the
|
||||
bus module reads an empty one as "this module asks nothing now", design 25 § 12.2). A tool the indexer does not serve
|
||||
yet answers "not known", and nothing retires on it. It is marked retiring only once no node-engine reports applied a
|
||||
build that reads it, as the rename window is (§ above). mesh-config then emits `retiring` (family `config.events`,
|
||||
kept seven days) and asks the operator once, through the operator channel, whether to remove it, naming any node that
|
||||
still read it until lately; it is removed only on the operator's Approve. **mesh-config holds no secret**, so the
|
||||
retiring flow and its one ask stay mesh-config's, and on the Approve it removes a value itself and retires a secret
|
||||
through the seat `mesh-vault`'s new optional verb `retire` (one module's own secret, by node, module and name), carrying
|
||||
the operator's warrant, which the vault checks (ADR 0259 §6) before it removes anything. The vault's caller list for
|
||||
`retire`, a trusted config value in ADR 0363's form, names the holder of the seat `mesh-config` alone, checked against
|
||||
the caller the tool runner proved; the warrant is bound to the node, module and name it was asked for, so it retires
|
||||
nothing else. mesh-config then emits `retired` on `config.events` (module, name, node, the ask), and the vault emits
|
||||
`own-retired` (node, module, name, the ask's id, the caller it proved) beside its `rotated` and `own-replaced`, on the
|
||||
module events stream (ADR 0363) under the vault's own subjects, kept as that stream keeps every module's events. An ask unanswered for seven
|
||||
days is asked once more, and then written as a finding, cleared by an answer, a cancel, or the value read again; a
|
||||
sleeping node holding a retirement open for seven days is a finding naming it. `config_retiring` lists what is
|
||||
retiring, since when, and its ask, and asks it again or cancels it.
|
||||
|
||||
**Where mesh-config keeps it.** Its new state (`renames`, `retiring`, `findings`) and its stream `config.events` are
|
||||
declared in its old `state` and `streams` sections in this milestone, as the builder's and the checker's new state is
|
||||
(design 51 *The move*), so that neither mesh-config nor the deciding parts that ask it wait on the bus module's
|
||||
process, which is delivered in the same milestone. They move to bus state when each module next moves (ADR 0595 §11).
|
||||
`config-changed` stays on the shared events stream, unchanged, so its consumers do not move.
|
||||
|
||||
**How the rename and the retiring are checked**, as cases of mesh-config's first whole-module integration test (C20,
|
||||
C24 below), of the checker's and of the vault's existing whole-module tests: the checker's ask of `renames` meeting a
|
||||
holder that does not serve it refuses `renamed-from` naming the verb, one meeting an open window refuses dropping
|
||||
`renamed-from` naming the nodes, and a check that cannot ask fails saying so; `retire` with the warrant removes the
|
||||
secret and emits `retired` and `own-retired`, without it, with a warrant for another name, or from a caller not on the list it is
|
||||
refused; a waiter on the vault's `rotated` or `own-replaced` does not wake on `own-retired`; a node that was away,
|
||||
returning with a build that reads the new name, closes the rename window on the next `delivered` (until ADR 0618 is
|
||||
accepted); `config_rename_close` drops a node from a window and records the hand act; a window held seven days and a
|
||||
retirement held seven days each write their finding naming the node, cleared when it closes.
|
||||
|
||||
### The mesh delivery cases this design carries
|
||||
|
||||
@@ -740,3 +793,7 @@ what answers them are listed in design 51's *Reviewed by architectural domain*.
|
||||
value's trust (security), `packages` is a section of its own, not `uses`, and mesh-config hears the status view's
|
||||
`delivered` events (seams), a value retires only once no node-engine runs a build reading it (security), and `renamed`
|
||||
and `retiring` have a family and an ask that is asked again (events).
|
||||
|
||||
The milestone's amendment (ADR 0612, ADR 0613: the seat verb `renames` the checker asks, the vault's `retire`,
|
||||
`manifest-accepted` for every manifest, mesh-config's new state in its old sections) awaits its own review by
|
||||
architectural domain, which rewrites this section.
|
||||
|
||||
@@ -3,16 +3,33 @@ layer: to-be
|
||||
status: in-progress
|
||||
code:
|
||||
- mesh-controller cmd/mesh-controller (the verbs artifacts, collect, images and mirrors), internal/builder (copied bases), internal/inventory (artifact_mirrored)
|
||||
- mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its unbuilt code removed)
|
||||
- mesh-catalog modules/gitea (package retention)
|
||||
- mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its program, holding the two image seats)
|
||||
- mesh-catalog modules/gitea (package retention; the two package seats' verbs in its Go bundle)
|
||||
- mesh-catalog modules/mesh-build-go, modules/mesh-build-node, modules/mesh-build-image (publishing through the seats)
|
||||
- mesh-delivery modules/mesh-delivery-builder (versions, tag asks), the forge bridge, the indexer, the checker, the assigner
|
||||
- mesh-sdk (the bus helper for sending in parts)
|
||||
- mesh-catalog modules/docker (image pruning)
|
||||
updated: 2026-11-03
|
||||
- mesh-host internal/apply (every new image of a module pulled before any of its containers is replaced)
|
||||
updated: 2026-11-04
|
||||
decisions:
|
||||
- "ADR 0605"
|
||||
- "ADR 0612"
|
||||
- "ADR 0614"
|
||||
- "ADR 0613"
|
||||
- "ADR 0617"
|
||||
- "ADR 0606"
|
||||
- "ADR 0607"
|
||||
- "ADR 0610"
|
||||
- "ADR 0611"
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
- "ADR 0615"
|
||||
- "ADR 0620"
|
||||
- "ADR 0621"
|
||||
- "ADR 0346"
|
||||
- "ADR 0386"
|
||||
- "ADR 0564"
|
||||
- "ADR 0240"
|
||||
- "ADR 0311"
|
||||
- "ADR 0251"
|
||||
- "ADR 0189"
|
||||
@@ -49,22 +66,47 @@ asks work of another by an event of its own (ADR 0610 §1).
|
||||
Each is held once for the mesh. The last two replace the seat `mesh-artifact-store` and front the one artifact store;
|
||||
while one module holds both, both deliver the provision `artifact-store`, and splitting them between two holders takes a
|
||||
decision of its own. `mesh-artifact-store` stays served until every node-engine reports applied a build that reads the
|
||||
new names (ADR 0610 §4); a sleeping node holds it open. `distribution` closes it itself: it hears the status view's
|
||||
`delivered` events (and asks the status view's `node_haves` at its start, design 47), stops serving the old name once every
|
||||
node whose declaration names an artifact reports the new build, and its tool `registry_old_name` lists the nodes still
|
||||
holding it open. `cargo-package-registry` stays named, without a holder, until a
|
||||
build module publishes cargo packages.
|
||||
new names (ADR 0610 §4); a sleeping node holds it open. `distribution` closes it itself. The status view's
|
||||
`delivered` counts only the nodes present (an away node is normal, ADR 0603 §7), so it is only the wake-up, never the
|
||||
proof (ADR 0610 §4, its progressive insight of 2026-11-03): at its start and on each `delivered` event, `distribution`
|
||||
asks the status view's `node_haves` for every node, away ones included, and stops serving the old name once every node
|
||||
whose declaration names an artifact has applied a build whose node-engine states the capability to read the new names
|
||||
(ADR 0614 §4), judged by that stated capability and never by a fingerprint. Its tool `registry_old_name` lists the
|
||||
nodes still holding it open, and drops a retired node from the window by hand, recorded as a hand act; a node holding
|
||||
it open for seven days is a finding naming the node, cleared when the window closes. Closing the old name emits
|
||||
`old-name-closed` (the old seat name, the time, the nodes that held it last, and the nodes dropped by hand) on
|
||||
`distribution`'s own family `distribution.events`, kept seven days and capped at 16 MiB, dropping its oldest when
|
||||
full (it carries a handful of events per window), never on a seat's `<seat>.events`: it is one module's window, not
|
||||
the seats' contract. A node away when the last `delivered` was said should close the window when it returns and
|
||||
applies the new build, with no other delivery: what wakes `distribution` then is to be decided in ADR 0618
|
||||
(reserved), the node-engine seat's event for a change of a node's stated manifest-reads and the delivery view's read
|
||||
of them, never a watch of the node-engine's have state (ADR 0596). **Until ADR 0618 is accepted**, such a node closes
|
||||
the window on the next `delivered`, whichever delivery says it: `distribution` asks `node_haves` then, finds the
|
||||
returned node's new build, and closes. `cargo-package-registry` stays
|
||||
named, without a holder, until a build module publishes cargo packages.
|
||||
|
||||
**One contract, a seat definition** (design 26, *A seat's definition*). Verbs: `publish`, `publish-part` (ADR 0611), `versions`, `remove` (a
|
||||
tagged version only; the holder itself refuses `remove` of a version with no tag), and on `container-image-registry`
|
||||
`mirror` and `tags`. Events: `published`, `refused` (added to ADR 0605 §2's two, additively) and `removed`, each
|
||||
tagged version; the holder itself refuses `remove` of a version with no tag, but for the retention removal below), and
|
||||
on `container-image-registry` `mirror` and `tags`; the two seats `distribution` holds add `hold` and `release`,
|
||||
with the events `held` and `released`, for the artifact sweep (ADR 0617 §2), both verbs in the seat definition and
|
||||
in `distribution`'s caller list. The seats this milestone changes, these four, `mesh-broker` and the four it adds a verb or an event to, are defined
|
||||
whole, their contracts moved into `mesh-contracts` (ADR 0613 §1, ADR 0620; design 26). Events: `published`, `refused` (added to ADR 0605 §2's two, additively) and `removed`, each
|
||||
carrying the name, version, kind, tag, digest, fingerprint, commit and holder, and `refused` its reason; each seat's
|
||||
events are its own family, `<seat>.events`, kept seven days. A holder stores
|
||||
under the version it is given and counts nothing.
|
||||
|
||||
**Who may call which verb is the holder's to say**, in a trusted config value of the holder listing its callers, the
|
||||
form ADR 0363 gives a restricted family: `publish` and `publish-part` the build modules, and a part only from the
|
||||
proved caller that opened its transfer; `mirror` mesh-build-image; `remove` the builder.
|
||||
proved caller that opened its transfer; `mirror` mesh-build-image; `remove` the builder, for a tagged version, and the
|
||||
holder of the artifact sweep, for an untagged bundle, archive or copied base with the reason retention (ADR 0610 §3);
|
||||
`hold` (on the two seats `distribution` holds: keep one untagged bundle, archive or copied base that the artifact
|
||||
sweep's rules keep from removal by retention; never an image of its own) the module that runs the artifact sweep
|
||||
alone (ADR 0617 §2); `release` (let a hold go) that module, and the operator's session only through
|
||||
`registry_holds`, each release by hand recorded as a hand act. When `registry_holds`, `distribution`'s own tool,
|
||||
calls its own seat's `release`, the caller the holder proves is the one the tool runner proved and stamped on the
|
||||
`registry_holds` call, the operator's session (ADR 0386 §7), carried on to `release` by the tool runner's stamp and
|
||||
never by the tool's own words; `distribution`'s own account is not on `release`'s list, so the tool cannot release in
|
||||
its own name. Every removal, whoever asked it, emits `removed` with its reason.
|
||||
Declaring `invokes` alone grants nothing. The holder checks the caller identity the tool runner proved and stamps on
|
||||
the request (design 25 § 12.4), never a name in the request's body, and answers any other caller with a refusal naming
|
||||
the verb.
|
||||
@@ -86,9 +128,9 @@ the verb.
|
||||
- **The file travels in parts through the seat** (ADR 0611). `publish` opens a transfer, naming it and the digest of
|
||||
every part (an image's layers, a package's file, a bundle's blob); the holder answers the digests it lacks. The build
|
||||
module sends only those through `publish-part` (transfer, part digest, sequence, last), each at most 1 MiB, with
|
||||
mesh-sdk's bus helper for sending in parts, added to mesh-sdk's runtime in step 2's rebuild. A `publish` opening a
|
||||
mesh-sdk's bus helper for sending in parts, added to mesh-sdk's runtime in the milestone's one rebuild. A `publish` opening a
|
||||
transfer already open answers the parts held, so a cut transfer goes on from there. The parts land in the holder's
|
||||
own object store `incoming`, declared in its manifest with a cap of 20 GiB and a lifetime of 24 hours; a transfer with
|
||||
own object store `incoming`, asked for as bus state in its manifest (design 25 § 12) with a cap of 20 GiB and a lifetime of 24 hours; a transfer with
|
||||
no part for 24 hours is dropped and its parts deleted. The holder hashes every part and refuses one whose bytes do not
|
||||
match its digest. At 80 % of the cap it writes a finding (cleared below 80 %); at the cap it refuses a new transfer
|
||||
with `refused` naming the cap. Its tool `registry_incoming` lists open transfers and the fill, and drops one by hand.
|
||||
@@ -103,16 +145,77 @@ the verb.
|
||||
- **What is published when**: a package or an image from a trunk build under its version, or as a tagged version; a
|
||||
bundle or an archive from every done key by its digest alone, a candidate's included, and at the trunk's claim the
|
||||
build module calls `publish` with the version and the held digest, which stores nothing. An upstream image is copied
|
||||
by `mirror` with its pinned digest. The done key names the reference the seat answered; a key in the old
|
||||
`artifact-store://` form keeps it, every reader reads both forms, and none is built again for its form.
|
||||
- **No build module holds a registry's write credential**: the npm write token, the image push credential and the
|
||||
artifact store's write binding go in step 4. Each holder keeps its write side as an own secret beside a read-only
|
||||
tool token (the forge module: `write:package`, its tool token `read:package`).
|
||||
by `mirror` with its pinned digest.
|
||||
- **The done key names the reference the seat answered**, in ADR 0605 §9's form from this milestone on:
|
||||
`<registry>/<module>/<artifact>@sha256:<digest>` for an image, bundle, archive or upstream copy and
|
||||
`<name>@<version>` for a package. A key in the old `artifact-store://` form keeps it, every reader reads both forms,
|
||||
and none is built again for its form. A node-engine built before this milestone cannot read the new form, so a
|
||||
want whose build carries it reaches a node only once that node's engine states the capability to read both forms
|
||||
and both seat names; the assigner holds the node's want meanwhile and names the capability as the reason, and a
|
||||
node away keeps its want and catches up on return (ADR 0612 §2, made general by ADR 0614 §4).
|
||||
- **No build module holds a registry's write credential** (ADR 0605 §5). The npm write token goes. The image push
|
||||
credential and the artifact store's write binding that ADR 0605 §5 revokes do not exist today: the store takes
|
||||
writes from any process on the mesh network, so the rule protects nothing until its write side is closed. So
|
||||
`distribution`'s own program serves the store's port in front of the registry container, which listens on its
|
||||
node's loopback only: reads (the OCI distribution protocol's GET and HEAD) stay open to every node's container
|
||||
runtime, and a write reaches the store only from `distribution`'s own process, after a `publish` it accepted.
|
||||
**Until every build module publishes through the seats**, a window, the build modules'
|
||||
builds still running push straight to the store, as they do today, with no credential: today's store takes an
|
||||
unauthenticated OCI write from the mesh network, and no build module holds an image push credential. So
|
||||
`distribution`'s program keeps accepting such a write from the mesh network, no wider than today, until every
|
||||
node's have of every build module, read through `node_haves` at its start and on each `delivered`, runs a build
|
||||
that publishes through the seats; then it refuses every write but its own process's, naming `publish`, says so in
|
||||
its log and emits `direct-writes-closed` (the time, the build modules' builds last seen, the nodes dropped by hand)
|
||||
on `distribution.events`. While the window is open, a direct write to a tag the seat holds `published` is refused,
|
||||
naming the tag and `publish`, so an old build module cannot overwrite a version published through the seat. Since the
|
||||
window has no credential, closing it removes nothing from the store or the
|
||||
vault: it is the program's refusal alone. Its tool `registry_direct_writes` lists whether the window is open and
|
||||
the nodes whose have still runs an old build module, and drops a retired node from it by hand, recorded as a hand
|
||||
act; a node holding it open for seven days is `distribution`'s finding naming the node, cleared when the window
|
||||
closes. **While the program is down**, the store answers no read and no write: a node pulling an image fails and
|
||||
retries, a build module's write fails and its build says so, and what already runs is not touched. The holder's
|
||||
health check is declared in its manifest and run outside the program's process, as every module's (ADR 0240), so
|
||||
it still answers while the program is down: it probes the read side every minute and names the program. The
|
||||
program serves again at once when it restarts, holding nothing but its transfers in `incoming`. Each holder keeps its write side as an own secret beside a read-only tool token (the forge module: `write:package`, its
|
||||
tool token `read:package`).
|
||||
- **`distribution`'s first delivery** moves the registry container to its node's loopback and lets the program take
|
||||
the store's port, and it is the one delivery whose own containers serve every pull. Read on the node-engine's main
|
||||
(its apply of a container): the node-engine pulls a container's new image just before it replaces that container,
|
||||
then sets the running one aside, stopped, starts the new one, and starts the old one again if the new one does not
|
||||
run (issues 1742 and 1945). It does so one container at a time, so a module with two containers pulls its second
|
||||
image only after its first container was replaced; for `distribution` that second pull would meet the stopped
|
||||
registry. **So the node-engine pulls every new image of a module before it replaces any of its containers**, and a
|
||||
pull that fails replaces nothing, the module keeping what it ran: a change in the node-engine, a subtask of this
|
||||
milestone's node-engine work. The quick fix, a second port for this one delivery, is refused, since every module
|
||||
with more than one container meets the same order. `distribution`'s declaration lists the registry container before
|
||||
the program, so the port is free when the program starts. **What a pull meets in the swap**: from the registry
|
||||
container's stop until the program listens on the port, the store answers nothing. On main, a failed pull fails that
|
||||
module's apply, and the node agent tries a failed apply again only on the next change of the module's want or
|
||||
composition, on the node-engine's verb `compose` naming the module (by hand), or when its liveness judge finds a
|
||||
declared resource missing or down; only a failure of the bus itself is tried again on a clock (a bounded backoff,
|
||||
at most four times before the have says failed). So another module's apply whose pull falls in that window stays
|
||||
failed, its running containers kept, until one of those: its have says failed naming the image, and `compose`
|
||||
naming the module is its recovery.
|
||||
- **The package seats' read sides need no credential** for the mesh's own packages: the forge serves the mesh owner's
|
||||
packages to anonymous reads, so the provisions `npm-package-registry` and `go-package-registry` carry no
|
||||
credential (form none), and mesh-build-node, which held a pair for both writing and installing, requires none (the
|
||||
operator's answer of 2026-11-03, that the mesh's packages may be read without a credential). Public reads stay (the
|
||||
operator, 2026-11-04): the forge's package reads and the store's GET and HEAD answer anonymously on the mesh network
|
||||
and, where a public route serves them today, through it, the forge's public route included. The forge keeps serving
|
||||
the registry's public root (ADR 0346 §6, unchanged), so a lock file naming that root installs as today, and the lab
|
||||
trusts it as before (ADR 0346 §7).
|
||||
- **The forge module serves both package seats' verbs from its Go bundle**, the one that already serves its package
|
||||
retention, renamed for both packages; its tools bundle in TypeScript is unchanged, so no holder needs generated
|
||||
TypeScript types in this milestone.
|
||||
- **Go packages** are fetched through the Go seat's provision first and Go's public proxy second, with no `GOPRIVATE`
|
||||
and the checksum database off for the forge's paths; `go.sum` and the digest the builder records (Go's `h1:` hash,
|
||||
computed by the Go build module at the publish) refuse a mesh package with other content. The builder also tags
|
||||
each Go version in git (`go/v0.1.<n>`), so a user that names a version can fetch it from the forge's git as well.
|
||||
No mirror of outside modules is built; one could come later as a holder of a seat of its own.
|
||||
and the checksum database off for the forge's paths (ADR 0605 §6); `go.sum` and the digest the builder records (Go's
|
||||
`h1:` hash, computed by the Go build module at the publish) refuse a mesh package with other content. For this
|
||||
milestone the fetch keeps a third source last, the forge's git over the build module's own account (Go's `direct`):
|
||||
between the recipe change and the first numbered versions of mesh-sdk and `mesh-contracts` in the Go seat, a build
|
||||
by the new recipe could otherwise fetch nothing it names. The first change after those versions are in the Go seat
|
||||
removes `direct`, said in its pull request. The builder also tags each Go version in git (`go/v0.1.<n>`), so a user
|
||||
that names a version can fetch it from the forge's git as well. No mirror of outside modules is built; one could
|
||||
come later as a holder of a seat of its own.
|
||||
|
||||
### The versions
|
||||
|
||||
@@ -122,23 +225,50 @@ the verb.
|
||||
every earlier merge of the same name whose fingerprint the indexer still holds is numbered (a fingerprint replaced
|
||||
by a later merge before its claim is skipped, design 47 *After the merge*); a wait past 30 minutes writes a finding
|
||||
naming the merge it waits on. A fingerprint already numbered keeps its number.
|
||||
- **A package is numbered per source revision**, not per fingerprint: a second trunk build of a revision already
|
||||
numbered for that package (a build module's change reaching it by a second build, ADR 0603 §3, or the contracts
|
||||
move's one rebuild) keeps the package's version and publishes nothing, since a package's content is its source,
|
||||
but only when `versions` holds that revision `published`; a revision numbered but never stored is published by its
|
||||
next build under the same version. The build log says the version was kept, and why.
|
||||
So a publish that triggers a rebuild of the package's own build module (the shared contracts, the forge module
|
||||
that holds their registry and the Go build module that builds them, ADR 0614 §6) never comes round to a new
|
||||
version, and the triggers end. An image, bundle or archive is numbered per fingerprint, as above.
|
||||
- **The builder's state `versions`** holds one key per name, tag and version, with its fingerprint, commit, digest,
|
||||
time and state: `numbered`, `published`, `refused` with its reason, or `removed` with the time, the reason (a
|
||||
branch's end, retention, the sweep) and the event that removed it. History one, cap 1 GiB; past 80 % the builder
|
||||
writes a finding, cleared below it. Only the builder reaches it; others call `builder_versions` or hear its events. The build log
|
||||
branch's end, retention, the sweep) and the event that removed it. History one, cap 64 MiB, the most the template
|
||||
state allows (ADR 0595 §3). An estimate, not a count: about 30,000 keys a year (some 18,000 trunk versions, about
|
||||
50 numbered builds a day, and some 10,000 tagged ones) of about 0.5 KiB each, some 15 MiB a year, so the cap holds
|
||||
about four years at that rate; past 80 % the builder writes a finding, cleared below it. Only the builder reaches it; others call `builder_versions` or hear its events. The build log
|
||||
carries each change too.
|
||||
- **The floor**: on a name's first claim, or when `versions` holds nothing for it, the count starts above the highest
|
||||
of the seat's `versions`, the package's highest git tag (the forge bridge's tool `bridge_tags`) and the highest
|
||||
pseudo-version a consumer requires (the indexer's tool `indexer_requirements`). The builder numbers nothing for that
|
||||
name until all three answered, and writes a finding naming the silent one meanwhile, cleared once all three answer. Today that gives Go `v0.1.14`
|
||||
and npm `0.1.18`; a name with none starts at `0.1.0`.
|
||||
name until all three answered (ADR 0606 §3), and writes a finding naming the silent one meanwhile, cleared once all
|
||||
three answer. Today that gives Go `v0.1.14` and npm `0.1.18`; a name with none starts at `0.1.0`.
|
||||
- **An unknown floor does not stall the build of an image, bundle or archive** (ADR 0617 §1 for an image). The floor of `distribution`'s own
|
||||
names is `distribution`'s `versions`, which only its new build serves, and the same holds for the forge module and
|
||||
the builder: waiting would never end. So a trunk build of an image, bundle or archive whose floor is not yet known
|
||||
is claimed with no number, so none travels in its build request, built unnumbered and published by its digest (as a
|
||||
bundle or archive is from every done key, ADR 0605 §3; an image so by ADR 0617 §1). Such fingerprints are numbered
|
||||
in merge order once the floor answers (ADR 0606 §1). What wakes the floor is an event of the source that was
|
||||
silent, never a timer: for the seat's `versions`, the holder's `published` on the builder's durable consumer of
|
||||
`<seat>.events`, which that by-digest publish itself says; for `bridge_tags`, the forge bridge's next message on
|
||||
its stream `forge`, which the builder already consumes; for `indexer_requirements`, the indexer's next
|
||||
`manifest-accepted`; and for each, the condition keeper's event clearing the silent module's health condition, on
|
||||
the seat `mesh-conditions`' family (ADR 0564 §3), so a source that recovers with nothing to say still wakes it. On
|
||||
any of these the builder asks the silent sources again; beside them, its tool `builder_floor_again` asks the three
|
||||
sources of one name again at once by hand, recorded as a hand act. Once all three answer it numbers the build and emits
|
||||
`numbered`, which the build module consumes to call `publish` with the version and the held digest, which stores
|
||||
nothing. A package's trunk build waits for its floor, since a package is published only under
|
||||
its version.
|
||||
- **Events of the builder**: `numbered` (name, version, tag, fingerprint, commit, head when it answers an ask), on its
|
||||
own family, kept seven days; a waiter on a build's end does not wake on it. **Tools of the builder**:
|
||||
`builder_versions` (a name's versions in order, one call), `builder_publish_again` (one fingerprint under its number,
|
||||
after a holder failed; when the build's file is gone it builds the fingerprint again under the same number, and the
|
||||
new digest replaces the recorded one only if no `published` was ever emitted for that number, else it takes a new
|
||||
number; it refuses a version refused for other bytes), `builder_tag` (ask a tagged version), `builder_tag_asks` (open asks and
|
||||
their state) and `builder_untag` (remove a branch's tagged versions).
|
||||
their state), `builder_untag` (remove a branch's tagged versions), `builder_floor_again` (ask a name's floor
|
||||
again by hand, a hand act) and `builder_old_form` (below).
|
||||
|
||||
### Tagged versions
|
||||
|
||||
@@ -146,22 +276,38 @@ the verb.
|
||||
tag), on a family of its own, `checker.asks`, kept seven days, apart from its verdicts, so a waiter on a verdict does
|
||||
not wake on it, for each head of a delivery group member that declares a package; a person asks through `builder_tag`. The
|
||||
builder consumes the event on a durable consumer and keeps the ask in its own state `tag-asks`, removed when its
|
||||
branch is merged or deleted. An ask the builder has not answered within five minutes is narrated in the build log
|
||||
branch is merged or deleted, or its pull request closed. An ask the builder has not answered within five minutes is narrated in the build log
|
||||
with what it waits on.
|
||||
- **Numbered below the next version** (`0.1.15-feat-x.2`), never the latest; npm keeps it under a dist-tag of its own
|
||||
and Go's `@latest` skips it. A package new in a group is numbered from its floor (`0.1.0-feat-x.1`).
|
||||
- **A group's check builds after `published`**: Go members by the tagged version, TypeScript members with their lock
|
||||
naming it and the registry's integrity; nothing is packed for a check, and a lone pull request's check publishes
|
||||
nothing. No `published` within the publish's bound (10 minutes) fails the check naming the package.
|
||||
- **Removed with its branch**: on the forge bridge's merged or deleted event, heard on a durable consumer, the builder
|
||||
- **Removed with its branch**: on the forge bridge's `merged` event, its pull `closed`, or its branch-deleted event,
|
||||
heard on a durable consumer, the builder
|
||||
calls each seat's `remove`. It acknowledges the event only once every seat emitted `removed`; until then it retries
|
||||
every minute and, past 15 minutes, writes a finding naming the seat. A version with no tag is not removed by a
|
||||
branch's end; retention may remove an old unreferenced one (below).
|
||||
- **The forge bridge says a branch's end**: beside its `merged`, `refused`, `open` and `pull` kinds on its stream
|
||||
`forge`, it says a branch-deleted kind, from the forge module's `forge.delete` event, which it consumes on a durable
|
||||
consumer as it consumes the pull events. Only the forge module's account may publish `forge.delete`, the subject
|
||||
being in its own namespace (design 32 § 2), so no other module can end a branch's tagged versions. Every existing
|
||||
consumer of `forge` ignores a kind it does not know. Its tool `bridge_tags` answers a package's git tags, for the
|
||||
floor.
|
||||
|
||||
### Retention
|
||||
|
||||
The retention of *The package registry* below, and the artifact sweep, keep working, now through the seats: each
|
||||
removal emits `removed` and the builder marks its entry, so `versions` never says `published` for bytes that are gone.
|
||||
With the store's write side closed, the artifact sweep (seat `mesh-artifact-sweep`, held by artifact-store-tools) no
|
||||
longer deletes or holds by writing to the store: it calls `remove` on the seat that holds the thing, with the reason
|
||||
retention, and `hold` to keep an untagged bundle, archive or copied base its rules keep from removal by retention,
|
||||
which then skips it (ADR 0617 §2), both granted to its module alone by `distribution`'s caller list; its rules of what is kept (*The artifact store* below) stand, and it reads both reference forms.
|
||||
`distribution` keeps the holds in its own state `holds` (bus state, template state), emits `held` (the thing's
|
||||
name, kind and digest, the caller that asked, the time) and `released` (the same, and who let it go: the sweep, or
|
||||
the operator's session by hand) on the seat's `<seat>.events`, and its tool `registry_holds` lists what is held,
|
||||
since when and for whom, and releases one by hand through the seat's `release`, recorded as a hand act. A `remove` or `hold` the holder refuses to the sweep writes the sweep's finding, naming the
|
||||
thing and the reason, cleared by its next run with no refusal.
|
||||
The Go seat keeps what the npm seat keeps: every referenced version and the newest five of each package series. A
|
||||
Go version is referenced when a `go.mod` on a repository's default branch requires it; the forge module reads every
|
||||
`go.mod` as it reads lockfiles, and a `go.mod` it could not read stops a real run before anything is deleted, as an
|
||||
@@ -174,28 +320,79 @@ publish refused (the holder when it refuses, the builder when a build module's b
|
||||
is stored, or when a later version of the name is published), a registry seat not answering (each holder's health check,
|
||||
which calls `versions` on each seat it holds and probes the read side every minute, so a dead seat is seen with no
|
||||
publish running), a removal not done (the builder), a floor source silent (the builder), a store past 80 % (the holder,
|
||||
the builder). A build narrates its publish: asked, waiting on a seat, stored, refused.
|
||||
the builder), a node holding the old seat name open for seven days (`distribution`, cleared when the window closes), a node
|
||||
holding open the window of direct writes for seven days (`distribution`, the same), a node holding open the window of
|
||||
the node-engine's old-form done key for seven days (the builder, the same). A build narrates its publish: asked, waiting on a seat, stored, refused.
|
||||
|
||||
### The move, in four steps
|
||||
### The move, one milestone, ordered by the module graph
|
||||
|
||||
Each step is its own delivery, and no credential goes before its replacement works (ADR 0605 §7).
|
||||
The move is one milestone: one delivery group, one check, one merge (ADR 0612 §1, with design 26's seat definitions
|
||||
and design 32's manifest words; ADR 0613 §1 adds the bus module's work and the knowledge base's). Its order is not
|
||||
a sequence of deliveries but edges of the module graph, which the indexer keeps and each delivery part waits on by
|
||||
its own rule; nothing stores the order (ADR 0612 §2, ADR 0614 §1). The edges this move relies on:
|
||||
|
||||
1. A node-engine that reads both reference forms and both seat names; the holders serve every verb of the four seats
|
||||
with their caller lists, and `incoming`; the builder's `versions`, `tag-asks`, its
|
||||
events and tools; the forge bridge's `bridge_tags` and the indexer's `indexer_requirements`.
|
||||
2. Starts only once every node-engine reports step 1's build applied (a sleeping node holds it). Every build module
|
||||
publishes through the seats: mesh-sdk's first numbered publish, images, bundles, archives and
|
||||
upstream copies, each read back; delivered with the move of mesh-sdk's seat contracts into `mesh-contracts` (design
|
||||
26), so every module is rebuilt once for both. The checker emits `tag-wanted`. Until step 3, Go users fetch from the
|
||||
forge's git, where the builder's tags let them take the numbered version.
|
||||
3. The Go fetch switches to the Go seat. Its rollback stays safe after a user moved, since the git tags exist.
|
||||
4. The three credentials go, and `package-version.sum`. The old seat name retires after it, on the node-engines'
|
||||
reports.
|
||||
| Order | Edge | Kept by |
|
||||
|---|---|---|
|
||||
| A build module's build that publishes through a seat runs only after a holder build serving `publish` and `publish-part` runs healthy | served verb: each `invokes` entry of a seat verb or module tool is an edge to the module whose build serves it (ADR 0614 §2). The provision edge alone is empty here: today's holder builds already offer `npm-package-registry` and `artifact-store` | the assigner, counting the holder's own have as ADR 0603 §5 counts a provider's; the status view names the wait |
|
||||
| A holder's check of `builder_versions` and the builder's `remove` | the two calls close a cycle, so both verb edges on it are left out of the graph and the calls retry (ADR 0614 §3); the builder retries `remove` each minute with its 15-minute finding | the indexer's graph, naming each edge left out |
|
||||
| mesh-build-go's fetch from the Go seat only after the forge module serves its read side | provision: `go-package-registry`, offered only by the forge module's new build (ADR 0603 §5) | the assigner and the provisioner |
|
||||
| No credential goes before its replacement works | the build modules' manifests drop the write credentials in the same change that makes them publish through the seats, so the vault mints none for the build that publishes (ADR 0612 §2) | the served-verb edge above |
|
||||
| A build carrying the new reference form, or a manifest using this milestone's words or seat names, reaches a node only through a node-engine that reads them | a reader's capability the node-engine states, the baseline when it states none (ADR 0614 §4) | the assigner, holding that node's want and naming the capability |
|
||||
| The holders' `incoming`, `<seat>.events`, `distribution`'s `holds` and `distribution.events` exist before the holders start | bus state: each entry is a requirement on the provision `bus-state` of the bus module (ADR 0614 §5) | the assigner, as for any provision |
|
||||
| The shared contracts, the forge module that holds their registry and the build module that builds them | package and build module edges are triggers, never waits; the cycle they make holds nothing and is not refused (ADR 0614 §6); inside the group every member takes `mesh-contracts` and mesh-sdk by the group's head (the checker's group pin) | the indexer's graph |
|
||||
|
||||
Steps are rolled back in reverse order. Steps 1 to 3 are rolled back by reverting their pull request; step 4 by
|
||||
reverting it, which mints the build modules'
|
||||
credentials again. **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images with a
|
||||
one-off credential of the bootstrap, discarded once the holders serve `publish`.
|
||||
**How many builds** (ADR 0603 §3). The three build modules change in this milestone, so a module they build can
|
||||
be built up to three times in one window, and this milestone expects that count: once with the recipe that runs,
|
||||
once more when the agreement of the build nodes moves to the new build module, and once more when a first numbered
|
||||
publish of mesh-sdk or `mesh-contracts` triggers their users (ADR 0614 §6). The contracts move rides the first
|
||||
build and adds none (ADR 0613 §1). ADR 0614 bounds nothing here; a bound, such as a settle before building a
|
||||
fingerprint whose inputs still move, is a question for a record, put to the operator, and is not designed here.
|
||||
|
||||
**The node-engine's own build keeps the old reference form** until every node-engine states the capability to read
|
||||
the new one: a node-engine away across the milestone must be able to read the build it wakes to, or it could never
|
||||
update itself (ADR 0603 §7, §9). The builder writes the node-engine's done key in the old form until the stated
|
||||
capabilities show it on every node; this is a window like the old seat name's. What should wake the builder for a
|
||||
node that returns is to be decided in ADR 0618 (reserved): the node-engine seat's event for a change of a node's stated
|
||||
manifest-reads and the delivery view's read of them, never a watch of the node-engine's have state (ADR 0596). **Until
|
||||
ADR 0618 is accepted**, the builder hears the status view's `delivered` events, asks `node_haves` at its start and on
|
||||
each, and a node that was away closes the window on the next `delivered`. Its tool `builder_old_form` lists whether the
|
||||
window is open and the nodes whose node-engine does not yet state the capability, and drops a retired node from it by
|
||||
hand, recorded as a hand act; a node holding it open for seven days is the builder's finding naming the node, cleared
|
||||
when the window closes. Closing it says so in the build log and emits `old-form-closed` (the time, the nodes that held
|
||||
it last, the nodes dropped by hand) on the builder's own family beside `numbered`; a waiter on a build's end does not
|
||||
wake on it.
|
||||
|
||||
**When the group is pushed.** Only once the release that adds the node-engine's stated capability (design 47) runs on
|
||||
every node, read from the status view's haves before the push, and once the progressive insights of ADR 0595 and
|
||||
ADR 0597 this design is written to are accepted. No release of the judge goes ahead: the indexer and the assigner
|
||||
serve their first views as tools of their own, in a form the running judge already takes in (ADR 0621 §4; design 25
|
||||
§ 12.2). A node away for weeks does not hold the push for ever: it is left
|
||||
out of that read only by a recorded hand act naming the node and why, as `registry_old_name` drops one, said in the
|
||||
group's pull request; the node then takes the new node-engine as any away node does when it returns.
|
||||
|
||||
**The milestone's own check** runs the changed build modules (ADR 0603 §10) against the running holders, which serve
|
||||
no `publish`: a changed build module whose manifest invokes a verb no running build serves is one the check cannot
|
||||
run, and the modules it builds are composed from their manifests, the verdict naming them and the verb (ADR 0614 §7).
|
||||
**The foundation's deciding parts** (the builder, the checker, the indexer, the forge bridge) keep their new state
|
||||
(`versions`, `tag-asks`, the stream `checker.asks` and the like) in their old `state` and `streams` sections in this
|
||||
milestone, so none of them waits on the bus module's process, which needs them first; the holders ask theirs as bus
|
||||
state.
|
||||
|
||||
**Before the merge, the operator is told** two things that come with it: mesh-build-node no longer requires
|
||||
`npm-package-registry`, so its provision key goes retiring and the provisioner asks the operator once per consumer
|
||||
(ADR 0570 §5); and closing the store's write side changes nothing the operator sees. `package-version.sum` and its
|
||||
guard go in the same change (ADR 0606 §6, read with ADR 0612 §1).
|
||||
|
||||
**Rolled back by one revert** (ADR 0612 §4): reverting the milestone's merge brings back the build modules'
|
||||
manifests that require the write credentials, so the vault mints them again as own secrets on the revert's delivery,
|
||||
and the builds that publish through the seats go with it. The old seat name still retires on the node-engines'
|
||||
reports (ADR 0610 §4). The revert's order is the module graph's, as the move's is: `distribution`'s reverted build
|
||||
is delivered before a build module's reverted build only where that build module's reverted manifest requires or
|
||||
invokes something only `distribution`'s reverted build offers; the milestone's prepare step checks the build
|
||||
modules' manifests for such an edge. The done keys written in the new form stay in the builder's state after a
|
||||
revert and are not built again for their form; whether an older reader can read them is to be decided in ADR 0618
|
||||
(reserved). **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images
|
||||
with a one-off credential of the bootstrap, discarded once the holders serve `publish`.
|
||||
|
||||
### Acceptance tests
|
||||
|
||||
@@ -213,9 +410,10 @@ test).
|
||||
after the build's file is gone builds again under the same number; with the seat's holder stopped at a first claim
|
||||
nothing is numbered and the finding names it; two merges claimed in reverse order are numbered in merge order; a
|
||||
retention removal turns the entry `removed`.
|
||||
- **Builder, added in round 2.** Two merges close together, the first replaced before its claim: the second is
|
||||
numbered at once; a build module's bound running out writes the builder's finding, and a later version of the name
|
||||
clears it; `builder_publish_again` after a `published` takes a new number.
|
||||
- **Builder, added in round 2** (integration, cases of the builder's whole-module test). Two merges close together,
|
||||
the first replaced before its claim: the second is numbered at once; a build module's bound running out writes the
|
||||
builder's finding, and a later version of the name clears it; `builder_publish_again` after a `published` takes a
|
||||
new number.
|
||||
- **Holder (gitea, distribution).** A caller not on the caller list is refused for each verb, and one module naming
|
||||
another as the caller in the request's body is refused; a part sent by a module other than its transfer's opener is
|
||||
refused; a transfer cut in the middle resumes from the parts held,
|
||||
@@ -223,13 +421,62 @@ test).
|
||||
survives retention, and an unreadable `go.mod` stops the run; a part whose bytes do
|
||||
not match its digest is refused; only the missing layers are sent; `remove` of a version with no tag is refused;
|
||||
with no publish running, a Go seat that stops answering while its holder runs writes its finding and clears it when it
|
||||
answers (a holder down whole is its module's own health check's); `registry_incoming`
|
||||
answers (a holder down whole is its module's own health check's); `registry_incoming`
|
||||
lists and drops.
|
||||
- **Vault.** Reverting step 4 mints the build modules' credentials again; the bootstrap's one-off credential is gone
|
||||
once the holders serve `publish`.
|
||||
- **Live.** Step 2 waits while one node has not got step 1's build, as `node_haves` shows (the lead's gate before
|
||||
step 2's merge); `registry_old_name` names the nodes
|
||||
holding the old name open.
|
||||
- **Holder, added for the milestone, unit** (table cases of `distribution`'s caller list and store front): a write to
|
||||
the store's port from any account but `distribution`'s process is refused, and a read is answered; the artifact
|
||||
sweep's `remove` with the reason retention is accepted and the same call from another caller is refused; a `hold`
|
||||
of an image or from another caller is refused; `release` from a caller not on the caller list is refused, a
|
||||
`release` carrying the operator's session as the proved caller is accepted and `distribution`'s own account as
|
||||
the caller is refused; while the direct-write window is open, a direct write to a tag the seat holds `published` is
|
||||
refused naming the tag and `publish`.
|
||||
- **Holder, added for the milestone, integration** (cases of `distribution`'s and the forge bridge's whole-module
|
||||
tests): the sweep's `remove` removes an untagged bundle and emits `removed`; `hold` keeps an archive from
|
||||
retention's removal, emits `held` and is listed by `registry_holds`, which releases it with `released` and records
|
||||
its hand act; a refused sweep `remove` writes the sweep's finding and its next clean run clears it; the forge bridge
|
||||
says a branch-deleted kind for the forge module's `forge.delete`, a `forge.delete` from another account is refused
|
||||
by the bus, and an existing consumer of `forge` ignores the new kind; with the program stopped a read fails and the
|
||||
holder's finding is written, a write fails too, and after its restart the read is answered and the finding
|
||||
cleared; a build module's old build pushing straight to the store is accepted until every node runs a build
|
||||
module publishing through the seats, and refused after, naming `publish`, with `direct-writes-closed` emitted and
|
||||
its log line written; `registry_direct_writes` names the node still running an old build module, and drops it by
|
||||
hand with a hand act; a node that was away, returning with the new build, closes the old seat name's window on
|
||||
the next `delivered` (until ADR 0618 is accepted); a waiter on a build's `published` does not wake on `held`,
|
||||
`released`, `old-name-closed` or `direct-writes-closed`.
|
||||
- **Builder, added for the milestone, integration** (cases of the builder's whole-module test): with the seat's
|
||||
holder down at a first claim, a bundle's trunk build is built unnumbered and published by its digest, the holder's
|
||||
`published` makes the builder ask the floor again and number it, and the build module, hearing `numbered`,
|
||||
publishes the version with the held digest; a package's trunk build waits; with `bridge_tags` silenced (a floor
|
||||
source other than the publish target) the floor finding is written and cleared once it answers, and with
|
||||
`bridge_tags` still silent after the by-digest publish, the build stays unnumbered until the forge bridge's health
|
||||
clears (no timer runs), and is then numbered and published by version; `builder_floor_again` asks the three
|
||||
sources at once and records the hand act; the node-engine's done key stays in the old form while one node's
|
||||
node-engine lacks the capability and is written in the new form once every node states it, `builder_old_form`
|
||||
naming that node meanwhile and dropping it by hand with a hand act; a node that was away, returning with the
|
||||
capability, closes that window on the next `delivered` (until ADR 0618 is accepted), with `old-form-closed` emitted
|
||||
and its build-log line written, and a waiter on a build's end does not wake on it; with the image's own seat silent
|
||||
at the claim of `distribution`'s own image, the image is still built, published by its digest, and numbered once
|
||||
its new build answers (ADR 0617 *How it is checked*).
|
||||
- **Builder, added for the milestone, unit** (cases of the builder's numbering): a second trunk build of a package's
|
||||
revision held `published` keeps its version and publishes nothing, while one numbered but refused is published by
|
||||
its second build under the same version.
|
||||
- **Node-engine, unit** (a case of its apply test, with the runner it already fakes): a module of two containers
|
||||
whose second new image cannot be pulled replaces neither container, says the image, and keeps both running; with
|
||||
both images pullable, both are pulled before the first container is set aside.
|
||||
- **Checker.** A changed build module invoking a verb no running holder serves is not run, and the verdict names the
|
||||
modules composed from their manifests instead, and the verb.
|
||||
- **Vault.** Reverting the milestone's merge mints the build modules' credentials again; the bootstrap's one-off
|
||||
credential is gone once the holders serve `publish`.
|
||||
- **Live.** The status view shows each build module's new build waiting on its holder's build serving `publish`
|
||||
until that runs healthy (ADR 0612 *How it is checked*); no build module's provision key holds a write credential;
|
||||
`registry_old_name` names the nodes holding the old name open, an away node among them. After the delivery:
|
||||
mesh-sdk's first numbered version is `published` on the Go seat and an image is read back by its digest, both
|
||||
shown by `builder_versions`; a node runs a module from a reference in the new form; a node-engine away across the
|
||||
milestone wakes and applies the new node-engine; the node-engine's apply journal for `distribution` on each node
|
||||
shows every new image pulled before its first container was replaced, and no node's pull of any module failed
|
||||
during the delivery (each node's haves, none failed naming an image); mesh-build-node installs from a lock file
|
||||
naming the registry's public root with no credential. Go's `direct`: its removal is named on the milestone's issue,
|
||||
and the change that removes it adds to the Go build module's recipe test a case that refuses `direct` in the fetch.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
@@ -250,6 +497,10 @@ and ADR 0611 (the file travels in parts through the seat). What was found, and w
|
||||
| health | conditions raised by their observers; no health check of a seat between publishes; a refused publish with no clearing | findings for the condition keeper (ADR 0564); the holder's health check; *Health and conditions* (each finding with what clears it) |
|
||||
| events and logs | removal and `published` failing silently; a publish silent for 10 minutes; events with no family or retention | the builder's durable consumers and acknowledgement rule; narration every five minutes; each event's family and seven days |
|
||||
|
||||
The milestone's amendment (ADR 0612 to ADR 0614: one milestone ordered by the module graph, the new reference form
|
||||
from it on, the store's write side closed, anonymous package reads, the floor and source-revision rules, the sweep
|
||||
through the seats) awaits its own review by architectural domain, which rewrites this section.
|
||||
|
||||
## The parts
|
||||
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-catalog, hq]
|
||||
updated: 2026-11-03
|
||||
updated: 2026-11-04
|
||||
decisions:
|
||||
- "ADR 0290"
|
||||
- "ADR 0321"
|
||||
@@ -13,6 +13,8 @@ decisions:
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
- "ADR 0597"
|
||||
- "ADR 0616"
|
||||
- "ADR 0613"
|
||||
---
|
||||
|
||||
# 55 — The knowledge base
|
||||
@@ -36,6 +38,10 @@ commit rule fall away (ADR 0597 §12, changing ADR 0592 §1 to §12, §17, §25,
|
||||
`requires` by template (ADR 0595), and no other module reads it: another module asks the knowledge base's tools or
|
||||
hears its events (ADR 0596). The ledger itself, the same for every module, is designed in
|
||||
[design 25](25-the-bus-on-nats.md) § 12; how a module asks for it, in [design 32](32-what-a-module-declares.md) § 4.
|
||||
|
||||
**Amended, 2026-11-04** (ADR 0616; the operator, 2026-11-03, on issue 1968: "I'm starting to lose the overview"). The
|
||||
knowledge base serves a read-only overview page for the operator, built from its own ledger and three other modules'
|
||||
verbs and events (§ 11), in the milestone that moves it onto the ledger (ADR 0613 §1).
|
||||
§ 1, § 1.1, § 2.1, § 4, § 8.1, § 9 and § 10 say what changes here.
|
||||
|
||||
**The mesh keeps its decision records and research efforts whole in a module of its own, `mesh-knowledge`, one
|
||||
@@ -93,7 +99,9 @@ and the knowledge base works out each design's state (ADR 0423, ADR 0424).
|
||||
│ imported, drafted, confirmed, returned
|
||||
├ consumes the router's warrants: the operator's Approve for a record that needs it (§ 5)
|
||||
├ consumes claude-code's judged: the judge's answer to a part (§ 5)
|
||||
└ consumes the delivery module's delivery-done announcement: a design's state (§ 9)
|
||||
├ consumes the delivery module's delivery-done announcement: a design's state (§ 9)
|
||||
├ consumes the issue tracker's, mesh-delivery's and the router's events for the overview page (§ 11)
|
||||
└ serves the overview page, read-only, on a port it declares, through the route proxy (§ 11)
|
||||
|
||||
mesh-issues (control-node) the counters for decision and research numbers (§ 6);
|
||||
the issues' design links, read for a design's state (§ 9)
|
||||
@@ -519,7 +527,8 @@ The records leave the store once, and the database is dropped only after a check
|
||||
|
||||
1. **Order.** The ledger is built first, in the order of ADR 0595 §10: mesh-sdk's typed `requires`, binding and
|
||||
package of the new calls; the indexer's judge's rules; the bus module's process, verbs and events; the tool
|
||||
runner's calls, `apply` among them, with its ledger credential per module; then mesh-knowledge moves, asking for
|
||||
runner's calls, `apply` among them, with its ledger credential per module assignment (ADR 0597 §9, as its
|
||||
progressive insight of 2026-11-03 has it, waiting for the operator's Approve); then mesh-knowledge moves, asking for
|
||||
its ledger, states and stream in `requires`. An indexer whose judge predates the new form refuses a manifest
|
||||
that uses it, and the placement holds the module off a machine whose tool runner does not read the typed
|
||||
binding, so mesh-knowledge cannot be delivered before them. The release sdk1 (issue 1585, ADR 0593 §10), which
|
||||
@@ -768,10 +777,92 @@ done:
|
||||
no state `names`, and the bus holds none for it once the move's check is clean; its tools and health name the
|
||||
ledger's repairs and lag, and no digest store; the consumers of `reviews` still hear it.
|
||||
|
||||
## 11. The overview page
|
||||
|
||||
**The knowledge base serves one read-only web page for the operator** (ADR 0616 §1), so the state of the work is one
|
||||
address away rather than an answer read in chat. It shows three blocks: every design in progress with its milestones
|
||||
and their issues (the tree the `progress` verb answers); the decisions and research efforts accepted in the last seven
|
||||
days and those waiting (proposed, or waiting for the operator's Approve); and what waits on the operator: records
|
||||
waiting for an Approve, the operator channel's open asks and the delivery groups waiting for an Approve. The seven days
|
||||
are counted from the ledger's dates when the page or its tool answers, never kept as a window in the model. What
|
||||
reached which nodes is not on it: a merged group no longer waits on the operator.
|
||||
|
||||
```
|
||||
mesh-knowledge's process
|
||||
├ its own ledger records, their status and dates (no first view needed)
|
||||
├ issue tracker first view: milestones, list then its events
|
||||
├ seat mesh-delivery first view: groups then group, transition, merged
|
||||
├ seat operator-channel first view: asks then the router's offered, closed
|
||||
│ (a restricted stream granted to the knowledge base alone)
|
||||
▼
|
||||
the page's model, in memory ──▶ the page (GET only) ──▶ route proxy ──▶ the operator
|
||||
└▶ its tool (the model as data)
|
||||
```
|
||||
|
||||
- **Built from mesh paths alone** (ADR 0616 §2). Besides its own ledger, the model comes from three other modules, each through
|
||||
a verb for the first view and events after it: the issue tracker's `milestones` and `list` and its events; the seat
|
||||
`mesh-delivery`'s `groups` and its `group`, `transition` and `merged` events, a group leaving the block on `merged`;
|
||||
and the seat `operator-channel`'s `asks` and the router's events `offered` and `closed`, an ask entering the block on
|
||||
`offered` and leaving it on `closed`. It never hears an asker's `decided` family. It reads no other module's bus
|
||||
state (ADR 0596 §2), and its manifest holds no `reads` entry, which the judge refuses (ADR 0595 §8).
|
||||
- **The router says when an ask starts and ends** (ADR 0616 §7). The router holding `operator-channel` emits `offered` when it
|
||||
offers an ask on a channel, and `closed` once for every end of an ask: answered, expired, cancelled by its asker,
|
||||
replaced by a newer ask about the same subject, or ended by the router itself on an `unlink` or `allow-link`. Each
|
||||
carries the ask's id, asker, headline and the channels it was offered on, and `closed` how it ended; never an option,
|
||||
a token or a warrant's proofs, so neither says more than `asks` already answers. Both go on a restricted stream of
|
||||
the router (ADR 0363 §4), heard only by a module the router's trusted config value `readers` grants, which names the
|
||||
knowledge base alone; `decided` stays read by its asker only. The stream is of the template events: kept seven
|
||||
days, capped at 64 MiB, dropping its oldest when full, so a page down longer than a week rebuilds from `asks`.
|
||||
- **Current on events, never polled** (ADR 0616 §3). Each source's block is built once at start from its verb, its first view,
|
||||
and then kept in memory from that source's events. A verb is asked again only for a source marked unanswered.
|
||||
- **A source that does not answer says so, never shows as empty** (ADR 0616 §6). A source is marked unanswered for one of two
|
||||
causes, named in the mark: its verb did not answer within its bound, or its subscription missed the bus's idle
|
||||
heartbeat past its bound, so a lost subscription or a silent source is found, not taken for a quiet one. Its block,
|
||||
on the page and in the tool's answer, then says so with the time of its last answer (the verb's last answer or the
|
||||
subscription's last event or heartbeat, whichever is later). A mark from the verb: the verb is asked again on a
|
||||
bounded backoff (from 10 seconds, doubling, at most every 5 minutes), and its first answer rebuilds the block and
|
||||
clears the mark. A mark from the subscription: nothing is asked while it is silent; its next event or heartbeat
|
||||
makes the module ask the verb once, to rebuild the block with what it missed, and that answer clears the mark (an
|
||||
unanswered verb then turns the mark into one from the verb). These are the only asks the page makes after start.
|
||||
- **It writes nothing** (ADR 0616 §4). The page has no form and no verb behind it and answers GET only. It is served by the
|
||||
module's own process on a port the module declares, reached through the route proxy by a route the module
|
||||
requires, which only the operator may open. The port listens `from: machine`, the narrowest listening scope the
|
||||
manifest has today, so a connection from another node is refused. That scope does not name a module: every local
|
||||
process of the knowledge base's node reaches the port, not only the route proxy, until issue 1968 gives a scope
|
||||
that grants one module's process. That scope is a change to mesh-sdk's manifest types, so it rebuilds every module
|
||||
that imports them, and it rides a later milestone's one rebuild rather than adding one here. A route proxy runs on
|
||||
the knowledge base's node (read from placement, 2026-11-04), so the page is reachable through it; a route proxy on
|
||||
another node cannot reach a port listening `from: machine`, so a page placed where none runs is unreachable until
|
||||
issue 1968.
|
||||
- **Every number on it can be read by a tool** (ADR 0616 §5): a tool of the knowledge base answers the page's model as data,
|
||||
marked reads, so the page shows nothing a tool cannot read (tools-are-how-a-module-is-spoken-to); a second tool,
|
||||
marked acts, asks one source's verb again by hand and rebuilds its block, recorded as a hand act.
|
||||
- **Health.** The module's health carries the page's last rebuild and each source's last answer, and is degraded while
|
||||
any source is unanswered.
|
||||
- **Built after the ledger store**: the page reads the records from the ledger (§ 2.1), so it is built after the
|
||||
knowledge base's ledger store, in the same milestone (ADR 0613 §1).
|
||||
|
||||
How it is checked (ADR 0616 *How it is checked*): a unit test builds the model from fixture issues, milestones,
|
||||
records, groups and asks and sees the three blocks; a fixture `group` or `transition` event changes the delivery block
|
||||
and `merged` takes the group out; `offered` adds an ask and `closed` takes it out; no verb is asked again in either
|
||||
case; a record accepted eight days before the answer's time is not in the seven-day block, with no event between. The
|
||||
manifest has no `reads` entry, names `groups` and `asks` among what it invokes, and consumes no `decided` family. The
|
||||
handler refuses any method but GET, and the route's grant names only the operator. The tool's answer equals the model
|
||||
the page renders. For each source, a verb failing at start marks it unanswered from the verb on the page, in the tool
|
||||
and in the health, never as an empty list, asked again on the backoff alone, its first answer clearing every mark;
|
||||
and a subscription stopping its heartbeat marks it unanswered from the subscription, asks nothing while silent, and
|
||||
asks the verb once on its next event or heartbeat. In the router, an ask offered emits `offered` once and each of its
|
||||
five ends emits `closed` once with how it ended, with no option, token or proof in either body; the knowledge base
|
||||
hears the stream and another module, an asker among them, is refused. A connection to the page's port from another node
|
||||
is refused (what `from: machine` gives; a local process other than the route proxy is not refused before issue 1968). The re-read tool rebuilds one source's block and asks no other. Live: after its delivery the operator opens the
|
||||
page and sees design 47's milestones, rendered headless and looked at before it is handed over.
|
||||
|
||||
## What it leaves out
|
||||
|
||||
Semantic or vector search; a browser view (its reading of the buckets is announced, ADR 0592); line comments on a proposal (the judgement's findings name the section and
|
||||
clause instead); and the rules of how-we-build, which are not decision records. Each needs a decision of its own.
|
||||
Semantic or vector search; a browser view other than the overview page of § 11 (its reading of the buckets is
|
||||
announced, ADR 0592); line comments on a proposal (the judgement's findings name the section and clause instead),
|
||||
and any form or writing from the overview page; and the rules of how-we-build, which are not decision records. Each
|
||||
needs a decision of its own.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ playbook 01's *Review by architectural domain*, two architectural domains each:
|
||||
`review-health-and-events`; the lead starts all four side by side and rules on their findings. A fifth,
|
||||
`research-reader`, carries one reading task of playbook 01's step 5: code on main, counts from the manifests, a
|
||||
try in process. Four more carry playbook 07: `mesh-preparer` (the prepare step), `mesh-developer` (develop),
|
||||
`mesh-code-reviewer` (the review) and `mesh-watcher` (every wait). Every agent starts from the knowledge base and maps
|
||||
`mesh-code-reviewer` (the review) and `mesh-watcher` (every wait). A tenth, `hq-design-writer`, writes one set of design amendments
|
||||
(playbook 02, *Amending an existing design*) from accepted decisions and a milestone's findings, in a worktree of its
|
||||
own, ready for the review by architectural domain. Every agent starts from the knowledge base and maps
|
||||
each interaction onto a mesh path; the lead writes only the handover, never an agent's way of working. A subagent runs in the session that started it, from hq: its way of working comes from its definition and hq's playbooks, its knowledge from the knowledge base; one given a coding task works in a worktree under hq's `.work/` and also reads the conventions of the repository it changes, and where those contradict hq, hq wins and the agent says so.
|
||||
|
||||
## The development cycle
|
||||
|
||||
Reference in New Issue
Block a user