The SDK part of novox/hq ADR 0307 (the runtime serves a key's kept values, oldest first, to whoever may read the bucket), clause 3: State(name) gains History(key) beside Get. With the runtime's mesh/state.history (mesh-tools) it lets the issue tracker carry every value its bucket still keeps into its log at the switch (ADR 0300 §6), the audit trail issue 502 is about.
What
go/state.go: type StateRevision struct { Key; Value json.RawMessage; Revision uint64; Op string; At time.Time } with JSON names key, value, revision, op, at, and func (s KeptState) History(key string) ([]StateRevision, error), one ask of mesh/state.history with {state, key} through the existing KeptState.ask (30 s timeout).
The runtime's array is handed back as it comes, oldest first; a delete keeps its null value and op delete. [] (and a null answer) is an empty, non-nil history. A refusal passes through as the runtime's error, as Get's does; a refused History returns nil.
go/README.md names it.
Checked
go/state_history_test.go, against a fake stdio runtime: the exact params (method mesh/state.history, only state and key); three kept values oldest first with revision, op and time (an offset time compared as an instant); a delete entry with value null; [] and null answers as an empty history; a state not issued refused with the runtime's reason.
sh merge-check.sh passes locally: TypeScript 37/37, gofmt, vet, every Go package.
Go only: no other language SDK touched.
Release
To be released together with the Log work (#21, ADR 0297) as go/v0.1.14: main has no tag since go/v0.1.13 (it also carries #18 go/health and #20 CallerOf). Not tagged here; tagging happens after merge.
The SDK part of novox/hq ADR 0307 (the runtime serves a key's kept values, oldest first, to whoever may read the bucket), clause 3: `State(name)` gains `History(key)` beside `Get`. With the runtime's `mesh/state.history` (mesh-tools) it lets the issue tracker carry every value its bucket still keeps into its log at the switch (ADR 0300 §6), the audit trail issue 502 is about.
## What
- `go/state.go`: `type StateRevision struct { Key; Value json.RawMessage; Revision uint64; Op string; At time.Time }` with JSON names `key, value, revision, op, at`, and `func (s KeptState) History(key string) ([]StateRevision, error)`, one ask of `mesh/state.history` with `{state, key}` through the existing `KeptState.ask` (30 s timeout).
- The runtime's array is handed back as it comes, oldest first; a delete keeps its `null` value and op `delete`. `[]` (and a `null` answer) is an empty, non-nil history. A refusal passes through as the runtime's error, as Get's does; a refused History returns nil.
- `go/README.md` names it.
## Checked
- `go/state_history_test.go`, against a fake stdio runtime: the exact params (method `mesh/state.history`, only `state` and `key`); three kept values oldest first with revision, op and time (an offset time compared as an instant); a delete entry with value `null`; `[]` and `null` answers as an empty history; a state not issued refused with the runtime's reason.
- `sh merge-check.sh` passes locally: TypeScript 37/37, gofmt, vet, every Go package.
- Go only: no other language SDK touched.
## Release
To be released together with the Log work (#21, ADR 0297) as **go/v0.1.14**: main has no tag since go/v0.1.13 (it also carries #18 go/health and #20 CallerOf). Not tagged here; tagging happens after merge.
media-importer on ace: passed — healthy 1 times over 41s
mesh-sdk on every machine running it: sent
nzbgeek on ace: passed — judged with media-importer: healthy 1 times over 41s
nzbget on ace: passed — judged with media-importer: healthy 1 times over 41s
youtube on ace: passed — judged with media-importer: healthy 1 times over 41s
mesh-issues on novox: passed — healthy 1 times over 24s
media-importer on the rest running it: sent
mesh-issues on the rest running it: sent
nzbgeek on the rest running it: sent
nzbget on the rest running it: sent
youtube on the rest running it: sent
Transitions
2026-10-12 08:18 (new) → proposed (announced): novox/mesh-sdk#22's head announced
2026-10-12 08:20 proposed → checked (checked): the verdict names this commit
2026-10-12 08:20 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-12 08:22 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-12 08:23 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-12 09:04 held → delivering (release): Operator's word 2026-10-12 (asked in the g14 session hq-a8): it builds mesh-sdk and sends it to no machine; held only because I merged it before the group's composed check ran for both heads (issue 529). Its group mate mesh-tools #29 passed its own checks and is delivered; mesh-catalog #228's walk waits behind it.
2026-10-12 09:07 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@d41d255f28ad` — **delivered** since 2026-10-12T09:07:49Z
**Delivery plan** — builds mesh-sdk, sent nowhere; no bus step
- tier 0: mesh-sdk
**Group** `adr-0307-state-history`, in order: novox/mesh-sdk@d41d255f28ad → novox/mesh-tools@3f0c1a6cec18
**Machines**
- media-importer on ace: passed — healthy 1 times over 41s
- mesh-sdk on every machine running it: sent
- nzbgeek on ace: passed — judged with media-importer: healthy 1 times over 41s
- nzbget on ace: passed — judged with media-importer: healthy 1 times over 41s
- youtube on ace: passed — judged with media-importer: healthy 1 times over 41s
- mesh-issues on novox: passed — healthy 1 times over 24s
- media-importer on the rest running it: sent
- mesh-issues on the rest running it: sent
- nzbgeek on the rest running it: sent
- nzbget on the rest running it: sent
- youtube on the rest running it: sent
**Transitions**
- 2026-10-12 08:18 (new) → proposed (announced): novox/mesh-sdk#22's head announced
- 2026-10-12 08:20 proposed → checked (checked): the verdict names this commit
- 2026-10-12 08:20 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-12 08:22 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-12 08:23 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-12 09:04 held → delivering (release): Operator's word 2026-10-12 (asked in the g14 session hq-a8): it builds mesh-sdk and sends it to no machine; held only because I merged it before the group's composed check ran for both heads (issue 529). Its group mate mesh-tools #29 passed its own checks and is delivered; mesh-catalog #228's walk waits behind it.
- 2026-10-12 09:07 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 shanks as build-1791793093395514218 (builds --log build-1791793093395514218).
---
duration_ms: 68.25019
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: 53.782704
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 11.732876
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: 47.573819
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: 1.636352
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 1.095797
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.857306
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-oBWwIc/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-12T08:20:23.803Z
# [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: 216.931066
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-1xylcw/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-12T08:20:24.035Z
# [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: 124.14137
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: 72.530541
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-y3WcaM/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-y3WcaM/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-y3WcaM/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] contributions file is not JSON (/tmp/prov-y3WcaM/postgres-database.json): SyntaxError: Unexpected end of JSON input; nothing applied or removed until it can be read
# [provisioner:postgres-database] /tmp/prov-y3WcaM/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-y3WcaM/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-y3WcaM/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-y3WcaM/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: 1307.310722
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: 222.049209
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: 70.275146
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.074073
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.891901
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: 300.804101
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: 35.723972
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.327242
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.713825
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.214982
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.25533
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.519271
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: 83.682363
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: 3209.384516
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.379574
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: 285.23088
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.213356
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3978.55594
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.141s
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 1.040s
? 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 `d41d255f`
- `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-1791793093395514218` (`builds --log build-1791793093395514218`).
```
---
duration_ms: 68.25019
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: 53.782704
type: 'test'
...
# Subtest: an addition acts on the first pass
ok 13 - an addition acts on the first pass
---
duration_ms: 11.732876
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: 47.573819
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: 1.636352
type: 'test'
...
# [tools] broken: contributor failed, exposing none: Error: no token
# Subtest: semver orders releases
ok 16 - semver orders releases
---
duration_ms: 1.095797
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.857306
type: 'test'
...
# [provisioner:analytics] webapp-anchor: no longer in /tmp/prov-oBWwIc/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-12T08:20:23.803Z
# [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: 216.931066
type: 'test'
...
# [provisioner:s3-bucket] mesh_anchor_ncloud: no longer in /tmp/prov-derived-1xylcw/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-12T08:20:24.035Z
# [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: 124.14137
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: 72.530541
type: 'test'
...
# [provisioner:postgres-database] contributions file unreadable (/tmp/prov-y3WcaM/postgres-database.json): ENOENT: no such file or directory, open '/tmp/prov-y3WcaM/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-y3WcaM/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] contributions file is not JSON (/tmp/prov-y3WcaM/postgres-database.json): SyntaxError: Unexpected end of JSON input; nothing applied or removed until it can be read
# [provisioner:postgres-database] /tmp/prov-y3WcaM/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-y3WcaM/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-y3WcaM/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-y3WcaM/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: 1307.310722
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: 222.049209
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: 70.275146
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.074073
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.891901
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: 300.804101
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: 35.723972
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.327242
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.713825
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.214982
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.25533
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.519271
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: 83.682363
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: 3209.384516
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.379574
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: 285.23088
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.213356
type: 'test'
...
1..37
# tests 37
# suites 0
# pass 37
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3978.55594
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.141s
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 1.040s
? git.novox.be/novox/mesh-sdk/go/example [no test files]
ok git.novox.be/novox/mesh-sdk/go/health 0.003s
```
Review (independent): APPROVE at d41d255f28, checked against ADR 0307. (The forge refuses an APPROVED review from a tool under ADR 0266, so this is a comment.)
Wire: pass. StateRevision (go/state.go, key, value, revision, op, at) matches mesh-tools StateKept (bus/state.go) field for field. History asks mesh/state.history with {state, key} alone (state_history_test.go:113). It reads [] and null as an empty, non-nil slice (state.go History: revisions := []StateRevision{}, and the null/empty check). Test at state_history_test.go:136. A delete's value reads as null when the runtime sends null, and as nil when the runtime omits it, which is what mesh-tools does; both satisfy the contract.
A refusal hands back the runtime's error and a nil slice (state_history_test.go:101-105, 139).
Nothing else in state.go changed; the diff only adds lines.
I ran merge-check.sh locally and it passed; TestHistoryIsEveryKeptValueOldestFirst passed. Forge checks at this head: mesh/merge-gate success 'pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)'; mesh/repo-check success 'pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)'. mesh/delivery-group is pending: 'group adr-0307-state-history checking: 0 of 2 member(s) ready'.
Non-blocking findings: none.
Review (independent): APPROVE at d41d255f28adf870f605c4e098e287b168eadaec, checked against ADR 0307. (The forge refuses an APPROVED review from a tool under ADR 0266, so this is a comment.)
1. Wire: pass. StateRevision (go/state.go, key, value, revision, op, at) matches mesh-tools StateKept (bus/state.go) field for field. History asks mesh/state.history with {state, key} alone (state_history_test.go:113). It reads [] and null as an empty, non-nil slice (state.go History: `revisions := []StateRevision{}`, and the null/empty check). Test at state_history_test.go:136. A delete's value reads as null when the runtime sends null, and as nil when the runtime omits it, which is what mesh-tools does; both satisfy the contract.
2. A refusal hands back the runtime's error and a nil slice (state_history_test.go:101-105, 139).
3. Nothing else in state.go changed; the diff only adds lines.
I ran merge-check.sh locally and it passed; TestHistoryIsEveryKeptValueOldestFirst passed. Forge checks at this head: mesh/merge-gate success 'pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)'; mesh/repo-check success 'pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)'. mesh/delivery-group is pending: 'group adr-0307-state-history checking: 0 of 2 member(s) ready'.
Non-blocking findings: none.
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.
The SDK part of novox/hq ADR 0307 (the runtime serves a key's kept values, oldest first, to whoever may read the bucket), clause 3:
State(name)gainsHistory(key)besideGet. With the runtime'smesh/state.history(mesh-tools) it lets the issue tracker carry every value its bucket still keeps into its log at the switch (ADR 0300 §6), the audit trail issue 502 is about.What
go/state.go:type StateRevision struct { Key; Value json.RawMessage; Revision uint64; Op string; At time.Time }with JSON nameskey, value, revision, op, at, andfunc (s KeptState) History(key string) ([]StateRevision, error), one ask ofmesh/state.historywith{state, key}through the existingKeptState.ask(30 s timeout).nullvalue and opdelete.[](and anullanswer) is an empty, non-nil history. A refusal passes through as the runtime's error, as Get's does; a refused History returns nil.go/README.mdnames it.Checked
go/state_history_test.go, against a fake stdio runtime: the exact params (methodmesh/state.history, onlystateandkey); three kept values oldest first with revision, op and time (an offset time compared as an instant); a delete entry with valuenull;[]andnullanswers as an empty history; a state not issued refused with the runtime's reason.sh merge-check.shpasses locally: TypeScript 37/37, gofmt, vet, every Go package.Release
To be released together with the Log work (#21, ADR 0297) as go/v0.1.14: main has no tag since go/v0.1.13 (it also carries #18 go/health and #20 CallerOf). Not tagged here; tagging happens after merge.
Delivery
novox/mesh-sdk@d41d255f28ad— delivered since 2026-10-12T09:07:49ZDelivery plan — builds mesh-sdk, sent nowhere; no bus step
Group
adr-0307-state-history, in order: novox/mesh-sdk@d41d255f28 → novox/mesh-tools@3f0c1a6cecMachines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
d41d255fmesh/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-1791793093395514218(builds --log build-1791793093395514218).Review (independent): APPROVE at
d41d255f28, checked against ADR 0307. (The forge refuses an APPROVED review from a tool under ADR 0266, so this is a comment.)revisions := []StateRevision{}, and the null/empty check). Test at state_history_test.go:136. A delete's value reads as null when the runtime sends null, and as nil when the runtime omits it, which is what mesh-tools does; both satisfy the contract.I ran merge-check.sh locally and it passed; TestHistoryIsEveryKeptValueOldestFirst passed. Forge checks at this head: mesh/merge-gate success 'pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)'; mesh/repo-check success 'pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript), go/merge-check.sh (go)'. mesh/delivery-group is pending: 'group adr-0307-state-history checking: 0 of 2 member(s) ready'.
Non-blocking findings: none.