0.1.12: retire a consumer after five stable passes, wait for a person over the bound (hq ADR 0230) #9

Merged
mesh-admin merged 2 commits from feat/retired-consumers into main 2026-10-06 12:51:36 +00:00
Contributor

runProvisioner now follows the retirement model of hq ADR 0230, in place of 0.1.11's hourly release.

Loop

  • Retires a set only after the same unasked set has held for 5 consecutive readable passes.
  • More than 3, or more than half of those held, waits for a person.
  • Approve accepts only the exact set waiting. Reject keeps the set active. A set the mesh asks for again settles. A retired consumer asked for again is re-enabled and announced.
  • An unreadable pass resets the count.

Adapter

  • New optional methods: retire, retired, delete, held.
  • retire falls back to remove, so existing adapters keep working unchanged.

Options and API

  • New options: announce (emits provisioner.retirement) and module (registers the four tools via registerModuleTools).
  • New export: provisionerTools(handle).
  • The returned stop function also carries retirement, approve, reject and deleteRetired.
  • Removed: withdrawAtOnce, withdrawFraction, releaseEveryMs. Nothing in the catalogue passes them.

Tests: 33 pass (12 new in test/retire.test.ts); tsc is clean.

TS providers. All 11 use ^0.1.x with no lockfile, so each picks this up on its next build: redis, mosquitto, minio, umami, mssql, mongodb, influxdb, mesh-vault, mailu, gitea, cloudflare-dns.

  • They get the stable count and the threshold for free.
  • To be approvable, each must pass { module, announce }. That is one line where it already imports emit.
  • To be cleanable, each needs retired/delete in its adapter.
  • Those provider bumps are not in this PR. Until they land, a set over the bound in a TS provider waits, safely, with no approval path except a restart.

The module option assumes the runtime loads provisioner/index.js into the same process that collects tool registrations; that is unverified.

Rollout: merge, then publish 0.1.12. The go/ module is unchanged, so no go/ tag is needed.

`runProvisioner` now follows the retirement model of hq ADR 0230, in place of 0.1.11's hourly release. **Loop** - Retires a set only after the same unasked set has held for 5 consecutive readable passes. - More than 3, or more than half of those held, waits for a person. - Approve accepts only the exact set waiting. Reject keeps the set active. A set the mesh asks for again settles. A retired consumer asked for again is re-enabled and announced. - An unreadable pass resets the count. **Adapter** - New optional methods: `retire`, `retired`, `delete`, `held`. - `retire` falls back to `remove`, so existing adapters keep working unchanged. **Options and API** - New options: `announce` (emits `provisioner.retirement`) and `module` (registers the four tools via registerModuleTools). - New export: `provisionerTools(handle)`. - The returned stop function also carries `retirement`, `approve`, `reject` and `deleteRetired`. - Removed: `withdrawAtOnce`, `withdrawFraction`, `releaseEveryMs`. Nothing in the catalogue passes them. **Tests:** 33 pass (12 new in test/retire.test.ts); tsc is clean. **TS providers.** All 11 use `^0.1.x` with no lockfile, so each picks this up on its next build: redis, mosquitto, minio, umami, mssql, mongodb, influxdb, mesh-vault, mailu, gitea, cloudflare-dns. - They get the stable count and the threshold for free. - To be approvable, each must pass `{ module, announce }`. That is one line where it already imports emit. - To be cleanable, each needs `retired`/`delete` in its adapter. - Those provider bumps are not in this PR. Until they land, a set over the bound in a TS provider waits, safely, with no approval path except a restart. The `module` option assumes the runtime loads provisioner/index.js into the same process that collects tool registrations; that is unverified. **Rollout:** merge, then publish 0.1.12. The go/ module is unchanged, so no go/ tag is needed.
mesh-admin added 1 commit 2026-10-06 12:25:48 +00:00
The hourly release let a mistaken bulk unassignment drain one consumer an hour
with nobody deciding. Per novox/hq ADR 0230 a consumer is retired (disabled,
marked, data kept) only after the same unasked set in five consecutive passes;
more than 3, or more than half of those held, waits for approve or reject.
Deletion is a separate act of a person, of a retired consumer only.
jschoubben added 1 commit 2026-10-06 12:35:52 +00:00
Five passes are 25 s, shorter than a real hiccup. And a provider's remove can
destroy — a ledger file, a public DNS record — so an adapter without a retire
of its own is mark-only: the consumer keeps its access, is marked and said, and
remove is reached only by a person's delete (hq ADR 0230).
Author
Contributor

Updated in 0704908. This supersedes two points in the description above.

  1. Stability now needs two conditions. A consumer is retired only after 5 consecutive identical passes and after the same unasked set has held for stableForMs. The default is STABLE_FOR_MS = 10 min. An unreadable pass restarts both.
  2. Mark-only. remove is no longer called to retire.
    • New optional adapter pair: retire (disable) and reenable. reenable runs before create when a retired consumer is asked for again.
    • An adapter without retire is mark-only. Nothing changes in its backend and the consumer keeps its access. The loop records it as retired with access: "kept". It is logged loudly (RETIRED, MARK ONLY ... KEEPS ITS ACCESS) and announced.
    • provisioner_retirement reports retires_by: mark-only and stable_for_seconds.
    • remove is reached only through a person's provisioner_delete, and only when the adapter has no delete.

All 11 catalogue TS providers are mark-only until they add retire: redis, mosquitto, minio, umami, mssql, mongodb, influxdb, mesh-vault, mailu, gitea and cloudflare-dns.

A mark-only mark lives in the provider's process, so a restart forgets it.

Tests: 36/36 pass, run twice. New tests:

  • five passes are not enough without the hold, and the default hold is 10 minutes;
  • mark-only never calls remove on retire, and remove runs only on delete;
  • retire/reenable work as a pair.

The sdk.test withdrawal tests were rewritten so remove runs only on delete. tsc is clean.

Updated in 0704908. This supersedes two points in the description above. 1. **Stability now needs two conditions.** A consumer is retired only after 5 consecutive identical passes **and** after the same unasked set has held for `stableForMs`. The default is `STABLE_FOR_MS` = 10 min. An unreadable pass restarts both. 2. **Mark-only.** `remove` is no longer called to retire. - New optional adapter pair: `retire` (disable) and `reenable`. `reenable` runs before `create` when a retired consumer is asked for again. - An adapter without `retire` is **mark-only**. Nothing changes in its backend and the consumer keeps its access. The loop records it as retired with `access: "kept"`. It is logged loudly (`RETIRED, MARK ONLY ... KEEPS ITS ACCESS`) and announced. - `provisioner_retirement` reports `retires_by: mark-only` and `stable_for_seconds`. - `remove` is reached only through a person's `provisioner_delete`, and only when the adapter has no `delete`. **All 11 catalogue TS providers are mark-only until they add `retire`:** redis, mosquitto, minio, umami, mssql, mongodb, influxdb, mesh-vault, mailu, gitea and cloudflare-dns. A mark-only mark lives in the provider's process, so a restart forgets it. Tests: 36/36 pass, run twice. New tests: - five passes are not enough without the hold, and the default hold is 10 minutes; - mark-only never calls `remove` on retire, and `remove` runs only on delete; - `retire`/`reenable` work as a pair. The sdk.test withdrawal tests were rewritten so `remove` runs only on delete. tsc is clean.
mesh-admin merged commit a5fc8308d4 into main 2026-10-06 12:51:36 +00:00
mesh-admin deleted branch feat/retired-consumers 2026-10-06 12:51:36 +00:00
Author
Contributor

Delivery novox/mesh-sdk@e08a10f55f5b — held since 2026-10-06T23:05:28Z

Held for a person: merged without a passing check: only a person decides that it goes on — mesh-delivery.release with why lets it go on.

Delivery plan — builds mesh-sdk, sent nowhere; no bus step

  • tier 0: mesh-sdk

Transitions

  • 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-sdk#9 merged; its head was not heard by this owner, so it is made from the forge's word
  • 2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-sdk@e08a10f55f5b` — **held** since 2026-10-06T23:05:28Z Held for a person: merged without a passing check: only a person decides that it goes on — `mesh-delivery.release` with why lets it go on. **Delivery plan** — builds mesh-sdk, sent nowhere; no bus step - tier 0: mesh-sdk **Transitions** - 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-sdk#9 merged; its head was not heard by this owner, so it is made from the forge's word - 2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-sdk#9