Issue 386: a module waiting for an operator's secret could only answer not healthy, so its first-node gate failed and the operator was told there was nothing to do.
Adds go/health (standard library only): Answer{state, healthy, why, waits}, Wait{part, secret|setting, what}, the constructors Healthy, Unhealthy, Waiting, and Read, which the node-engine uses. healthy stays beside state, so an older engine reads a wait as not healthy, as before. A wait Read cannot check (none, neither or both names, not a name, no what or part, more than one line, a path or an address) makes the answer unhealthy with why, so it never excuses anything. A test per refusal and per state.
Issue 386: a module waiting for an operator's secret could only answer not healthy, so its first-node gate failed and the operator was told there was nothing to do.
Adds `go/health` (standard library only): `Answer{state, healthy, why, waits}`, `Wait{part, secret|setting, what}`, the constructors `Healthy`, `Unhealthy`, `Waiting`, and `Read`, which the node-engine uses. `healthy` stays beside `state`, so an older engine reads a wait as not healthy, as before. A wait Read cannot check (none, neither or both names, not a name, no what or part, more than one line, a path or an address) makes the answer unhealthy with why, so it never excuses anything. A test per refusal and per state.
hq: novox/hq#294 (ADR 0283).
A module waiting for the operator's secret had only 'not healthy' to say,
so its first-node gate failed and the operator was told nothing to do.
The answer gains a state and named waits, and Read refuses any wait it
cannot check, so a malformed wait never excuses a fault.
mesh-issues on novox: passed — healthy 3 times over 2m30s
mesh-sdk on every machine running it: sent
mesh-issues on the rest running it: sent
Transitions
2026-10-10 19:10 (new) → proposed (announced): novox/mesh-sdk#18's head announced
2026-10-10 19:13 proposed → checked (checked): the verdict names this commit
2026-10-10 19:13 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-10 19:15 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-10 19:15 published → held (hold): something holds it for a person: its group's composed check did not pass for the heads that merged, or it merged and no walk was opened for it
2026-10-10 19:21 held → delivering (release): operator's go in chat (2026-10-10 21:24): mesh-sdk #18 builds the library and is sent to no machine, so going on changes nothing that runs; its partner mesh-catalog #190 stays rejected until fixed; releasing unblocks mesh-catalog #189 (issue 399) and mesh-controller #207 in the same walk
2026-10-10 19:24 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-sdk@33917f91ac3c` — **delivered** since 2026-10-10T19:24:34Z
**Delivery plan** — builds mesh-sdk, sent nowhere; no bus step
- tier 0: mesh-sdk
**Group** `feat/386-a-wait-for-the-operator`, in order: novox/hq@2648d571b347 → novox/mesh-sdk@33917f91ac3c
**Machines**
- mesh-issues on novox: passed — healthy 3 times over 2m30s
- mesh-sdk on every machine running it: sent
- mesh-issues on the rest running it: sent
**Transitions**
- 2026-10-10 19:10 (new) → proposed (announced): novox/mesh-sdk#18's head announced
- 2026-10-10 19:13 proposed → checked (checked): the verdict names this commit
- 2026-10-10 19:13 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-10 19:15 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-10 19:15 published → held (hold): something holds it for a person: its group's composed check did not pass for the heads that merged, or it merged and no walk was opened for it
- 2026-10-10 19:21 held → delivering (release): operator's go in chat (2026-10-10 21:24): mesh-sdk #18 builds the library and is sent to no machine, so going on changes nothing that runs; its partner mesh-catalog #190 stays rejected until fixed; releasing unblocks mesh-catalog #189 (issue 399) and mesh-controller #207 in the same walk
- 2026-10-10 19:24 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
Delivery plan — builds mesh-sdk, sent nowhere; no bus step
tier 0: mesh-sdk
Run by the build seat on g14 as build-1791659225641187265 (builds --log build-1791659225641187265).
ok 11 - a waiting set the mesh asks for again settles, and nothing is retired
---
duration_ms: 67.051978
type: 'test'
...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
---
duration_ms: 62.581998
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 17.294178
type: 'test'
...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
---
duration_ms: 53.086581
type: 'test'
...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
---
duration_ms: 2.055653
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 0.855241
type: 'test'
...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
---
duration_ms: 0.553603
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-lTpcGC/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:09:06.114Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
---
duration_ms: 218.232114
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-AXwcCF/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:09:06.349Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
---
duration_ms: 134.172367
type: 'test'
...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
---
duration_ms: 82.791438
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-Kn7IKq/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-Kn7IKq/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-Kn7IKq/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-Kn7IKq/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-Kn7IKq/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-Kn7IKq/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-Kn7IKq/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
---
duration_ms: 1322.894813
type: 'test'
...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
---
duration_ms: 223.056811
type: 'test'
...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
---
duration_ms: 71.382656
type: 'test'
...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
---
duration_ms: 403.547688
type: 'test'
...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
---
duration_ms: 402.21129
type: 'test'
...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
---
duration_ms: 302.827637
type: 'test'
...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
---
duration_ms: 40.251042
type: 'test'
...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
---
duration_ms: 0.267002
type: 'test'
...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
---
duration_ms: 0.614518
type: 'test'
...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
---
duration_ms: 0.178887
type: 'test'
...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
---
duration_ms: 125.991566
type: 'test'
...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
---
duration_ms: 83.496336
type: 'test'
...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
---
duration_ms: 82.533142
type: 'test'
...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
---
duration_ms: 3214.439927
type: 'test'
...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
---
duration_ms: 84.100092
type: 'test'
...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
---
duration_ms: 274.026994
type: 'test'
...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
---
duration_ms: 4.107347
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3951.031234
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-sdk/go 0.117s
ok git.novox.be/novox/mesh-sdk/go/asker 0.002s
ok git.novox.be/novox/mesh-sdk/go/asks 0.003s
ok git.novox.be/novox/mesh-sdk/go/cli 0.005s
? git.novox.be/novox/mesh-sdk/go/example [no test files]
ok git.novox.be/novox/mesh-sdk/go/health 0.002s
**Merge check** at `622b6b4f`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
**Delivery plan** — builds mesh-sdk, sent nowhere; no bus step
- tier 0: mesh-sdk
Run by the build seat on g14 as `build-1791659225641187265` (`builds --log build-1791659225641187265`).
```
ok 11 - a waiting set the mesh asks for again settles, and nothing is retired
---
duration_ms: 67.051978
type: 'test'
...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
---
duration_ms: 62.581998
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 17.294178
type: 'test'
...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
---
duration_ms: 53.086581
type: 'test'
...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
---
duration_ms: 2.055653
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 0.855241
type: 'test'
...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
---
duration_ms: 0.553603
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-lTpcGC/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:09:06.114Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
---
duration_ms: 218.232114
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-AXwcCF/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:09:06.349Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
---
duration_ms: 134.172367
type: 'test'
...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
---
duration_ms: 82.791438
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-Kn7IKq/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-Kn7IKq/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-Kn7IKq/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-Kn7IKq/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-Kn7IKq/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-Kn7IKq/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-Kn7IKq/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
---
duration_ms: 1322.894813
type: 'test'
...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
---
duration_ms: 223.056811
type: 'test'
...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
---
duration_ms: 71.382656
type: 'test'
...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
---
duration_ms: 403.547688
type: 'test'
...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
---
duration_ms: 402.21129
type: 'test'
...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
---
duration_ms: 302.827637
type: 'test'
...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
---
duration_ms: 40.251042
type: 'test'
...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
---
duration_ms: 0.267002
type: 'test'
...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
---
duration_ms: 0.614518
type: 'test'
...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
---
duration_ms: 0.178887
type: 'test'
...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
---
duration_ms: 125.991566
type: 'test'
...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
---
duration_ms: 83.496336
type: 'test'
...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
---
duration_ms: 82.533142
type: 'test'
...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
---
duration_ms: 3214.439927
type: 'test'
...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
---
duration_ms: 84.100092
type: 'test'
...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
---
duration_ms: 274.026994
type: 'test'
...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
---
duration_ms: 4.107347
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3951.031234
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-sdk/go 0.117s
ok git.novox.be/novox/mesh-sdk/go/asker 0.002s
ok git.novox.be/novox/mesh-sdk/go/asks 0.003s
ok git.novox.be/novox/mesh-sdk/go/cli 0.005s
? git.novox.be/novox/mesh-sdk/go/example [no test files]
ok git.novox.be/novox/mesh-sdk/go/health 0.002s
```
Independent review, verdict REQUEST CHANGES at 622b6b4f55 (posted as a comment: the forge refuses REQUEST_CHANGES from a tool, ADR 0266).
sh go/merge-check.sh passes (gofmt, vet, all tests ok). Decision 1 of ADR 0283 is implemented: three states, healthy kept for older readers, an answer without state read from healthy, waits with no wait / neither / both / not a name / no what / no part / several lines / address read as unhealthy with the reason, waits beside a fault kept only when readable and never excusing. Standard library only, Go only, no retired glossary words. Setting and secret names in the catalogue's module.json files all match the name pattern.
Blocking:
go/health/health.go:80 - the path/address pattern misses common forms, so a what/part can carry what ADR 0283 decision 1 forbids. Probed: passes (not refused) for "the source (/mnt/games)", "the source "/mnt/games"", "the folder ~/games", "the password of 2001:db8::1", "on fe80::1%eth0", "the share at nas.lan:445", "\\nas\games" (UNC), "C:\Users\x". Suggest: a path start after whitespace OR an opening bracket/quote, with optional ~ or . prefix ((^|[\s(\["'<=])[~.]{0,2}/\S); \\\\ and \b[A-Za-z]:\\; IPv6 (two colons between hex groups); host:port (\b[a-z0-9-]+(\.[a-z0-9-]+)+:[0-9]{1,5}\b). Add a test case for each.
Non-blocking:
2. health.go:80 false positive: a version like "1.2.3.4" reads as an address. Acceptable; a dotted hostname without port ("nas.example.org") is not caught at all - say so in the doc comment or catch FQDNs.
3. health.go:134-136 - state: healthy with healthy: false is silently read healthy. A contradiction is a fault the module made; consider reading it unhealthy with why (failure must be loud).
4. health.go:69-74 - Waiting does not Check its waits, so a module author learns of a malformed wait only when the node-engine reads it. Consider having Waiting return Unhealthy with the Check error, as it does for no wait.
5. health_test.go - no case for a setting that is not a name (health.go:91) and none for an address in part (only a path). Values in what ("the password hunter2") cannot be detected by any pattern; the doc comment could say the single-line + no-address check is the only guard.
Independent review, verdict REQUEST CHANGES at 622b6b4f556da88f0f944f073a3cdeca8326ef03 (posted as a comment: the forge refuses REQUEST_CHANGES from a tool, ADR 0266).
sh go/merge-check.sh passes (gofmt, vet, all tests ok). Decision 1 of ADR 0283 is implemented: three states, `healthy` kept for older readers, an answer without `state` read from `healthy`, waits with no wait / neither / both / not a name / no what / no part / several lines / address read as unhealthy with the reason, waits beside a fault kept only when readable and never excusing. Standard library only, Go only, no retired glossary words. Setting and secret names in the catalogue's module.json files all match the name pattern.
Blocking:
1. go/health/health.go:80 - the path/address pattern misses common forms, so a `what`/`part` can carry what ADR 0283 decision 1 forbids. Probed: passes (not refused) for "the source (/mnt/games)", "the source \"/mnt/games\"", "the folder ~/games", "the password of 2001:db8::1", "on fe80::1%eth0", "the share at nas.lan:445", "\\\\nas\\games" (UNC), "C:\\Users\\x". Suggest: a path start after whitespace OR an opening bracket/quote, with optional ~ or . prefix (`(^|[\s(\["'<=])[~.]{0,2}/\S`); `\\\\` and `\b[A-Za-z]:\\`; IPv6 (two colons between hex groups); host:port (`\b[a-z0-9-]+(\.[a-z0-9-]+)+:[0-9]{1,5}\b`). Add a test case for each.
Non-blocking:
2. health.go:80 false positive: a version like "1.2.3.4" reads as an address. Acceptable; a dotted hostname without port ("nas.example.org") is not caught at all - say so in the doc comment or catch FQDNs.
3. health.go:134-136 - `state: healthy` with `healthy: false` is silently read healthy. A contradiction is a fault the module made; consider reading it unhealthy with why (failure must be loud).
4. health.go:69-74 - Waiting does not Check its waits, so a module author learns of a malformed wait only when the node-engine reads it. Consider having Waiting return Unhealthy with the Check error, as it does for no wait.
5. health_test.go - no case for a setting that is not a name (health.go:91) and none for an address in `part` (only a path). Values in `what` ("the password hunter2") cannot be detected by any pattern; the doc comment could say the single-line + no-address check is the only guard.
Independent re-review, verdict APPROVE at 33917f91ac (posted as a comment: the forge refuses an approval from a tool, ADR 0266).
sh go/merge-check.sh passes at this head (gofmt, vet, all tests ok, go/health included).
The blocking finding is resolved: I re-probed every form from the first review and each is now refused (paths in brackets and quotes, ~/ and ./, IPv6 including :: and a zone, host:port, UNC, drive paths, URLs, IPv4), and there is a test per form. The non-blocking points are also addressed: state healthy with healthy false is read unhealthy naming the contradiction, Waiting checks its waits, there are tests for an invalid setting name and an address in part, and the package comment says that a value in plain words cannot be caught.
Non-blocking, no change needed to merge:
go/health/health.go (the path alternative (^|[\s(\[{"'\x60])[~.]{0,2}/) has no character required after the slash, so "either / or" is refused. The first version required a non-space after it; /\S would restore that.
The host:port alternative refuses times and ratios ("from 10:30", "step 1:2"). Both false positives fail safe: the answer is read as unhealthy and says why, which is loud. A dotted hostname without a port ("nas.example.org") still passes; this is accepted.
Independent re-review, verdict APPROVE at 33917f91ac3cc213b879b32f3c70e2aa70395f98 (posted as a comment: the forge refuses an approval from a tool, ADR 0266).
sh go/merge-check.sh passes at this head (gofmt, vet, all tests ok, go/health included).
The blocking finding is resolved: I re-probed every form from the first review and each is now refused (paths in brackets and quotes, ~/ and ./, IPv6 including :: and a zone, host:port, UNC, drive paths, URLs, IPv4), and there is a test per form. The non-blocking points are also addressed: state healthy with healthy false is read unhealthy naming the contradiction, Waiting checks its waits, there are tests for an invalid setting name and an address in part, and the package comment says that a value in plain words cannot be caught.
Non-blocking, no change needed to merge:
1. go/health/health.go (the path alternative `(^|[\s(\[{"'\x60])[~.]{0,2}/`) has no character required after the slash, so "either / or" is refused. The first version required a non-space after it; `/\S` would restore that.
2. The host:port alternative refuses times and ratios ("from 10:30", "step 1:2"). Both false positives fail safe: the answer is read as unhealthy and says why, which is loud. A dotted hostname without a port ("nas.example.org") still passes; this is accepted.
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
Delivery plan — builds mesh-sdk, sent nowhere; no bus step
tier 0: mesh-sdk
Run by the build seat on shanks as build-1791659424616013707 (builds --log build-1791659424616013707).
ok 11 - a waiting set the mesh asks for again settles, and nothing is retired
---
duration_ms: 65.417214
type: 'test'
...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
---
duration_ms: 57.631984
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 12.05773
type: 'test'
...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
---
duration_ms: 45.397321
type: 'test'
...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
---
duration_ms: 0.846112
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 1.027124
type: 'test'
...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
---
duration_ms: 0.71811
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-rUp2Ud/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:12:57.989Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
---
duration_ms: 220.273152
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-YzugZf/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:12:58.224Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
---
duration_ms: 132.283858
type: 'test'
...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
---
duration_ms: 71.337658
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-j4Q0Jv/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-j4Q0Jv/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-j4Q0Jv/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-j4Q0Jv/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-j4Q0Jv/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-j4Q0Jv/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-j4Q0Jv/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
---
duration_ms: 1315.34723
type: 'test'
...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
---
duration_ms: 210.630076
type: 'test'
...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
---
duration_ms: 71.125125
type: 'test'
...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
---
duration_ms: 402.373274
type: 'test'
...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
---
duration_ms: 401.313829
type: 'test'
...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
---
duration_ms: 302.255379
type: 'test'
...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
---
duration_ms: 34.968114
type: 'test'
...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
---
duration_ms: 0.321214
type: 'test'
...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
---
duration_ms: 0.76416
type: 'test'
...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
---
duration_ms: 0.216553
type: 'test'
...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
---
duration_ms: 127.962978
type: 'test'
...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
---
duration_ms: 85.370816
type: 'test'
...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
---
duration_ms: 82.236001
type: 'test'
...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
---
duration_ms: 3201.585035
type: 'test'
...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
---
duration_ms: 83.271636
type: 'test'
...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
---
duration_ms: 287.855217
type: 'test'
...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
---
duration_ms: 2.406024
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3972.235681
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-sdk/go 0.142s
ok git.novox.be/novox/mesh-sdk/go/asker 0.003s
ok git.novox.be/novox/mesh-sdk/go/asks 0.003s
ok git.novox.be/novox/mesh-sdk/go/cli 0.006s
? git.novox.be/novox/mesh-sdk/go/example [no test files]
ok git.novox.be/novox/mesh-sdk/go/health 0.003s
**Merge check** at `33917f91`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)
**Delivery plan** — builds mesh-sdk, sent nowhere; no bus step
- tier 0: mesh-sdk
Run by the build seat on shanks as `build-1791659424616013707` (`builds --log build-1791659424616013707`).
```
ok 11 - a waiting set the mesh asks for again settles, and nothing is retired
---
duration_ms: 65.417214
type: 'test'
...
# Subtest: delete refuses an active consumer and deletes only the named retired one
ok 12 - delete refuses an active consumer and deletes only the named retired one
---
duration_ms: 57.631984
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 12.05773
type: 'test'
...
# Subtest: what the backend holds at start is retired by the same rules
ok 14 - what the backend holds at start is retired by the same rules
---
duration_ms: 45.397321
type: 'test'
...
# Subtest: the four tools are registered for the module and answer
ok 15 - the four tools are registered for the module and answer
---
duration_ms: 0.846112
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 1.027124
type: 'test'
...
# Subtest: module tools register and collect, a thrower is skipped not fatal
ok 17 - module tools register and collect, a thrower is skipped not fatal
---
duration_ms: 0.71811
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-rUp2Ud/analytics.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so webapp-anchor KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:12:57.989Z
# [provisioner:analytics] webapp-anchor: DELETING, asked by someone: gone for good
# Subtest: provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
ok 18 - provisioner creates each consumer with the mesh's login and password; remove waits for a person's delete
---
duration_ms: 220.273152
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-YzugZf/store.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_ncloud KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): the mesh stopped asking for it: the same answer in 5 passes over 0s, from 2026-10-10T19:12:58.224Z
# [provisioner:s3-bucket] mesh_anchor_ncloud: DELETING, asked by someone: unassigned
# Subtest: provisioner hands the adapter what the mesh derived, and still has it at withdrawal
ok 19 - provisioner hands the adapter what the mesh derived, and still has it at withdrawal
---
duration_ms: 132.283858
type: 'test'
...
# Subtest: provisioner stops calling an unreadable secret a race, and says what to look at
ok 20 - provisioner stops calling an unreadable secret a race, and says what to look at
---
duration_ms: 71.337658
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-j4Q0Jv/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-j4Q0Jv/postgres-database.json'; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] contributions file is not JSON (/tmp/prov-j4Q0Jv/postgres-database.json): SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3); nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-j4Q0Jv/postgres-database.json is for s3-bucket, not postgres-database; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] /tmp/prov-j4Q0Jv/postgres-database.json has no given list; nothing applied or removed until it can be read
# [provisioner:postgres-database] contributions file readable again
# [provisioner:postgres-database] RETIREMENT WAITS FOR A PERSON: the mesh no longer asks for 2 of the 2 consumer(s) this provider holds (mesh_anchor_forge, mesh_anchor_mail), more than 3, or more than 50% of the 2 held. Nothing is retired until `retire approve` or `retire reject` (novox/hq ADR 0230)
# [provisioner:postgres-database] retirement of mesh_anchor_forge, mesh_anchor_mail approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_forge: no longer in /tmp/prov-j4Q0Jv/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_forge KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# [provisioner:postgres-database] mesh_anchor_mail: no longer in /tmp/prov-j4Q0Jv/postgres-database.json; RETIRED, MARK ONLY — this provider cannot disable a consumer (its adapter has no retire), so mesh_anchor_mail KEEPS ITS ACCESS and its data until a person deletes it with `cleanup delete`. Nothing was changed in the backend (novox/hq ADR 0230): approved by test: both unassigned on purpose
# Subtest: provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
ok 21 - provisioner removes nothing while its contributions file cannot be used (novox/hq issue 241)
---
duration_ms: 1315.34723
type: 'test'
...
# [provisioner:cache] webapp-anchor: the backend no longer holds it; applying again
# Subtest: provisioner applies again what the backend no longer holds, and trusts memory without holds
ok 22 - provisioner applies again what the backend no longer holds, and trusts memory without holds
---
duration_ms: 210.630076
type: 'test'
...
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# [provisioner:cache] webapp-anchor: could not check the backend, will ask again: Error: connection refused
# Subtest: provisioner keeps a consumer applied when the backend cannot be asked
ok 23 - provisioner keeps a consumer applied when the backend cannot be asked
---
duration_ms: 71.125125
type: 'test'
...
# Subtest: provisioner backs off when create does not satisfy holds, and says so
ok 24 - provisioner backs off when create does not satisfy holds, and says so
---
duration_ms: 402.373274
type: 'test'
...
# Subtest: provisioner treats a failed or hung holds as could-not-ask, and never logs the password
ok 25 - provisioner treats a failed or hung holds as could-not-ask, and never logs the password
---
duration_ms: 401.313829
type: 'test'
...
# Subtest: provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
ok 26 - provisioner stops a verify pass at a timeout, and retries a failed re-apply at the next check
---
duration_ms: 302.255379
type: 'test'
...
# Subtest: modules can SERVE: a real async tool, loaded and invoked over the broker
ok 27 - modules can SERVE: a real async tool, loaded and invoked over the broker
---
duration_ms: 34.968114
type: 'test'
...
# Subtest: serving refuses one module exposing two tools of the same name
ok 28 - serving refuses one module exposing two tools of the same name
---
duration_ms: 0.321214
type: 'test'
...
# Subtest: events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
ok 29 - events: a module emits, a listener and the audit sink (\#) both receive it, with metadata
---
duration_ms: 0.76416
type: 'test'
...
# Subtest: each registration is given the environment asked for it, and sees no other's (ADR 0192)
ok 30 - each registration is given the environment asked for it, and sees no other's (ADR 0192)
---
duration_ms: 0.216553
type: 'test'
...
# Subtest: a bundle on stdio lists its tools and answers a call, a seat's verb among them
ok 31 - a bundle on stdio lists its tools and answers a call, a seat's verb among them
---
duration_ms: 127.962978
type: 'test'
...
# Subtest: a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
ok 32 - a bundle served as its module lists the seat's verbs as the seat's, whichever registered first (ADR 0193)
---
duration_ms: 85.370816
type: 'test'
...
# Subtest: a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
ok 33 - a launched tool's emit is asked of the runtime, and the call answers once the runtime has published (ADR 0193)
---
duration_ms: 82.236001
type: 'test'
...
# Subtest: a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
ok 34 - a launched bundle subscribes as it is imported, handles events the runtime hands it, and asks through it (ADR 0198)
---
duration_ms: 3201.585035
type: 'test'
...
# Subtest: a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
ok 35 - a subscribe the runtime refused leaves no handler behind, so one event is handled once (novox/hq issue 276)
---
duration_ms: 83.271636
type: 'test'
...
# Subtest: a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
ok 36 - a launched bundle watches its state as it is imported, has the current values before it goes on, and keeps state through the runtime (ADR 0201)
---
duration_ms: 287.855217
type: 'test'
...
# Subtest: topics match as the runtime's do: * one segment, \# and ** any number
ok 37 - topics match as the runtime's do: * one segment, \# and ** any number
---
duration_ms: 2.406024
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3972.235681
NOT TESTED HERE: go/ (the Go binding) — the Go toolchain runs it from go/merge-check.sh
NOT TESTED HERE: python/, rust/, c/ — no toolchain of theirs runs a merge check
--- its go/merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-sdk/go 0.142s
ok git.novox.be/novox/mesh-sdk/go/asker 0.003s
ok git.novox.be/novox/mesh-sdk/go/asks 0.003s
ok git.novox.be/novox/mesh-sdk/go/cli 0.006s
? git.novox.be/novox/mesh-sdk/go/example [no test files]
ok git.novox.be/novox/mesh-sdk/go/health 0.003s
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Issue 386: a module waiting for an operator's secret could only answer not healthy, so its first-node gate failed and the operator was told there was nothing to do.
Adds
go/health(standard library only):Answer{state, healthy, why, waits},Wait{part, secret|setting, what}, the constructorsHealthy,Unhealthy,Waiting, andRead, which the node-engine uses.healthystays besidestate, so an older engine reads a wait as not healthy, as before. A wait Read cannot check (none, neither or both names, not a name, no what or part, more than one line, a path or an address) makes the answer unhealthy with why, so it never excuses anything. A test per refusal and per state.hq: novox/hq#294 (ADR 0283).
Delivery
novox/mesh-sdk@33917f91ac3c— delivered since 2026-10-10T19:24:34ZDelivery plan — builds mesh-sdk, sent nowhere; no bus step
Group
feat/386-a-wait-for-the-operator, in order: novox/hq@2648d571b3 → novox/mesh-sdk@33917f91acMachines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
622b6b4fmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)Delivery plan — builds mesh-sdk, sent nowhere; no bus step
Run by the build seat on g14 as
build-1791659225641187265(builds --log build-1791659225641187265).Independent review, verdict REQUEST CHANGES at
622b6b4f55(posted as a comment: the forge refuses REQUEST_CHANGES from a tool, ADR 0266).sh go/merge-check.sh passes (gofmt, vet, all tests ok). Decision 1 of ADR 0283 is implemented: three states,
healthykept for older readers, an answer withoutstateread fromhealthy, waits with no wait / neither / both / not a name / no what / no part / several lines / address read as unhealthy with the reason, waits beside a fault kept only when readable and never excusing. Standard library only, Go only, no retired glossary words. Setting and secret names in the catalogue's module.json files all match the name pattern.Blocking:
what/partcan carry what ADR 0283 decision 1 forbids. Probed: passes (not refused) for "the source (/mnt/games)", "the source "/mnt/games"", "the folder ~/games", "the password of 2001:db8::1", "on fe80::1%eth0", "the share at nas.lan:445", "\\nas\games" (UNC), "C:\Users\x". Suggest: a path start after whitespace OR an opening bracket/quote, with optional ~ or . prefix ((^|[\s(\["'<=])[~.]{0,2}/\S);\\\\and\b[A-Za-z]:\\; IPv6 (two colons between hex groups); host:port (\b[a-z0-9-]+(\.[a-z0-9-]+)+:[0-9]{1,5}\b). Add a test case for each.Non-blocking:
2. health.go:80 false positive: a version like "1.2.3.4" reads as an address. Acceptable; a dotted hostname without port ("nas.example.org") is not caught at all - say so in the doc comment or catch FQDNs.
3. health.go:134-136 -
state: healthywithhealthy: falseis silently read healthy. A contradiction is a fault the module made; consider reading it unhealthy with why (failure must be loud).4. health.go:69-74 - Waiting does not Check its waits, so a module author learns of a malformed wait only when the node-engine reads it. Consider having Waiting return Unhealthy with the Check error, as it does for no wait.
5. health_test.go - no case for a setting that is not a name (health.go:91) and none for an address in
part(only a path). Values inwhat("the password hunter2") cannot be detected by any pattern; the doc comment could say the single-line + no-address check is the only guard.Independent re-review, verdict APPROVE at
33917f91ac(posted as a comment: the forge refuses an approval from a tool, ADR 0266).sh go/merge-check.sh passes at this head (gofmt, vet, all tests ok, go/health included).
The blocking finding is resolved: I re-probed every form from the first review and each is now refused (paths in brackets and quotes, ~/ and ./, IPv6 including :: and a zone, host:port, UNC, drive paths, URLs, IPv4), and there is a test per form. The non-blocking points are also addressed: state healthy with healthy false is read unhealthy naming the contradiction, Waiting checks its waits, there are tests for an invalid setting name and an address in part, and the package comment says that a value in plain words cannot be caught.
Non-blocking, no change needed to merge:
(^|[\s(\[{"'\x60])[~.]{0,2}/) has no character required after the slash, so "either / or" is refused. The first version required a non-space after it;/\Swould restore that.mesh-admin referenced this pull request from novox/mesh-catalog2026-10-10 19:11:26 +00:00
Merge check at
33917f91mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-sdk)mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)Delivery plan — builds mesh-sdk, sent nowhere; no bus step
Run by the build seat on shanks as
build-1791659424616013707(builds --log build-1791659424616013707).