Commit Graph
919 Commits
Author SHA1 Message Date
jschoubben 5cfc4ff4a7 Trust the vault's key alone and write the grants part's user list alone
The controller ended by its unassignment and its lease is never handed, so
the engine's lease gate kept trusting the controller's key and the bus node
kept the controller's list as a fallback nobody writes (issue 1126, A6).
The engine now trusts no controller signature, its hand-over line names the
node seat's hand-over, and the user-list follower writes only the grants
part's list: a key unreadable or deleted keeps the list loaded, and with no
key heard nothing is written. Compositions in the node's bucket name the
file's place alone. The guard no longer asks for the controller's user.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 10:37:27 +01:00
mesh-tools 14a12cc7b6 Merge pull request 'The engine dials as the login node-logins names, rotates it after a test-dial, and enrols through the enrolment seat (issue 774)' (#195) from feat/l3-enrol into main 2026-11-02 07:56:45 +00:00
jschoubben 44db32c31b Pin mesh-sdk at dff0ce2, the enrolment group's rebased head, so every member builds the same contract (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@14a12cc7b6f9
mesh/delivery-group group feat/l3-enrol merged: every member is merged
mesh/check failed (2 tests): integration: mesh-mail-reader/cmd/mesh-mail-reader.TestAcceptanceOneMatteringMailSendsExactlyOneTellNamingIt: timed out w…
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:39 +01:00
jschoubben 12168ba4da The engine carries its login fall back in its health report, and says the enrolment fallback is the pre-lease path
A fall back to the old login during a rotation is now delivery.NodeHealth's Fallback (the node, the login fallen back from,
why, when), written with the node's health report, from which the watchman raises node-account-fallback; it is said no more
once the vault's key of the node moves on (blocker 7, ADR 0583 §6). mesh-host enrol's fall back to the controller's
enrolment subject says it is the path before the lease write, which the bus refuses once the lease is written. Tests: rows
in the existing rotation test; none added.

(issue 774)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:01 +01:00
jschoubben 72aca552e1 The engine's rotation against a real bus whose user list is reloaded step by step
A stand-in vault reloads a nats-server's users as the grants part's list is loaded, and the engine's own rotation code runs
against it with real test-dials and a real confirmation request: at every step at least one login authenticates; the engine
switches only once the list holds both logins and confirms in the new login's name; at rotation-done the old login is
retired and its value dropped; with the list never loaded the test-dial is refused and the engine never switches; and an
abandon after the switch brings the engine back to the old login before the new value goes (ADR 0583 §2-§6). One integration
test; mesh-host holds seven of fifty. The real vault and the real engine together are proven live on g14 first.

(issue 774, 774/S65)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:01 +01:00
jschoubben 9ae30a7bbb mesh-host enrol redeems through the seat mesh-enrolment and refuses an answer the token's key did not sign
The engine dials as enrol.<node> on the pinned certificate, calls redeem with the public halves of its keys and a proof over
them (the SDK's RedeemProof, which binds the node and the keys and never the token's secret), asks again while the holder
says to, and verifies the vault's answer against the key its token carries before it keeps anything: an answer signed by
another key, naming another signer or node, or altered is refused and nothing is kept. A signed one is kept (membership,
login, keys), then the node dials as the login it was given, reads its key of node-logins, and calls confirm; nothing is
applied until its placement names a module. When the bus does not serve the seat to the token (the controller still holds its
lease) the controller's enrolment subject is used as before. The token's shape is mesh-sdk's now: identity keeps its names as
aliases. Tests: one added, paid by merging three identity token tests into one (net two fewer).

(issue 774, 774/S64)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:01 +01:00
jschoubben e362c800a4 The engine test-dials, switches, confirms, drops and falls back across a rotation of its login
On node-logins' ready the engine fetches the new value through the vault's own hand-out (own/<node>/mesh-host/<login>, opened
with its sealing key), dials a second connection as the new login on the pinned certificate, and switches only if that
authenticates, then calls mesh-vault.node-account-confirm over the link as the new login (the vault is asked in the name of
the login the open link authenticated as, and only once that link is open). It keeps the old value until the key says the
rotation is done and drops it then, never before; an abandon, after the switch before or after the confirmation, switches
back before the new value is dropped; repeated refusals of the new login's credentials before rotation-done bring it back
to the old one, said as the finding node-account-fallback (in the journal: no route for an engine's finding to the watchman
is decided), and the same rotation is not tried again. Tests: two added, paid by merging two identity sealing tests into one;
more to be paid by deleting the identity token tests that move to mesh-sdk with the token (774/S64).

(issue 774, 774/S62, 774/S63)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:01 +01:00
jschoubben c7468656af The engine dials as the login the vault's node-logins names
A membership may name its Logins (ADR 0583 §1): the login in use and the password of each login held, kept beside the
identity, so a restart dials as the login in use. The engine follows its own key of node-logins as a watch on every link
and dials as the login it names when it holds its value, with the node's one inbox; with no key, or a key naming the first
login, it dials node.<n> with the membership's password as before. A change of the login in use drops the link so it is
opened again as the new one, and the vault is asked in the name of the login dialled (the subject names the caller).
Tests: one added, paid by merging the pinned-broker test with the malformed-pin test (two into one).

(issue 774, 774/S61)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 01:49:01 +01:00
mesh-tools 598b0d8193 Merge pull request 'The bus node writes the grants part's signed user list alone once the lease is handed (issue 774)' (#194) from feat/l3-removal into main 2026-11-02 00:35:43 +00:00
jschoubben c27fad5c6e Pin mesh-sdk at c21d8fd, the removal group's head, so every member builds the same contract (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@598b0d8193a5
mesh/delivery-group group feat/l3-removal merged: every member is merged
mesh/check failed: mesh-build-go: the check of novox/mesh-catalog#590: the ask mvyhumdm860c7f's step on shanks did not end within 30m0s of its start |…
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:53:42 +01:00
jschoubben 85826c633d The bus node's follower never falls back to the controller's user list once the grants part's key says the lease is handed (ADR 0571 §8, ADR 0547 §11)
A key that later cannot be read or is deleted is said, the key last heard stands and the list loaded stays. A key without
the flag still falls back, as before.
Tests: three rows added to the existing table test of the follower; no new test function. Built against the group's
mesh-sdk through a go.work outside the tree; go.mod is not re-pinned here.

(issue 774, 774/S31)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:23:11 +01:00
mesh-tools 9f7e898ecf Merge pull request 'Each node's engine writes its own declared packages and answers packages (ADR 0565 §6, issue 1953)' (#193) from feat/node-packages into main 2026-11-01 17:02:53 +00:00
jschoubben 74ecda8ea2 Pin the group's mesh-sdk head b2a27ed5 (issue 1953)
mesh/delivery merged: source.main moved to novox/mesh-host@9f7e898ecfeb
mesh/delivery-group group feat/node-packages merged: every member is merged
mesh/check checking 74ecda8ea2cf on bc960c51dbf0 | novox/mesh-host#193 in group feat/node-packages
The delivery group feat/node-packages pins one mesh-sdk head in every member: it carries DeclaredPackagesOf and the engine's read of the mirror this change uses.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 16:24:04 +01:00
jschoubben e61141f596 Keep the packages this node's compositions declare in the engine's own key, and answer them on the node seat (ADR 0565 clauses 6 and 15, issue 1953)
pacman still reads the controller's mirror because no engine wrote its key: the engine
now keeps each handed composition's package resources and, after a pass that handed
something new, writes mesh-host_packages.<node> only when the value differs, owed after
the growing bound when refused. The manifest declares the state so the grants part
creates it. The verb packages answers the key beside the mirror's key for the node and
their difference, and with write writes the key again from what was composed. H8's
integration test proves the write under the grant the grants part composes.

Needs mesh-sdk feat/node-packages (0ef7c0c) for DeclaredPackagesOf and the engine's read of the
mirror (go.mod pin left for the lead: the SDK commit is not pushed).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 16:09:13 +01:00
mesh-tools bc960c51db Merge pull request 'The node-engine takes the indexer's verdict and never ends a read on a refused manifest (ADR 0578 §3)' (#192) from feat/judge-once into main 2026-11-01 07:55:03 +00:00
jschoubben 07066fe7a0 Pin the group's mesh-sdk head after the gofmt fix
mesh/delivery merged: source.main moved to novox/mesh-host@bc960c51dbf0
mesh/delivery-group group feat/judge-once merged: every member is merged
mesh/check checking 07066fe7a0a8 on 233bb23bbcb1 | novox/mesh-host#192 in group feat/judge-once
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 03:27:07 +01:00
jschoubben 974177a074 Pin mesh-sdk after the first review round of the judge-once work (ADR 0578)
mesh/delivery-group group feat/judge-once checking: 6 of 6 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 974177a07478 on 233bb23bbcb1 | novox/mesh-host#192 in group feat/judge-once
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 03:13:14 +01:00
jschoubben 0422893873 Pin mesh-sdk at the final head of the judge-once work (ADR 0578)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 02:46:15 +01:00
jschoubben 1d3029fa1e Decode manifests in the packaging test, since the SDK's judge is deleted (ADR 0578)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 02:39:51 +01:00
jschoubben af66f72931 Take the indexer's verdict on a manifest instead of judging it, and name a held module (ADR 0578)
Issue 1585, S17: the engine decodes manifests leniently; a module whose manifest the indexer refused is held and named,
its data key stands, and every other module's items are still published. Policy, HoldsData and the certificate check no
longer parse with the judge.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 02:04:34 +01:00
mesh-tools 233bb23bbc Merge pull request 'The node-engine publishes each placed module's data items, so disk-load stops recomposing the node (ADR 0577)' (#191) from feat/data-items into main 2026-11-01 00:03:19 +00:00
jschoubben e031758e4a Forget a kept build when its key is written again, and retry data items on the composer's bound (review round 1)
mesh/delivery merged: source.main moved to novox/mesh-host@233bb23bbcb1
mesh/delivery-group group feat/data-items merged: every member is merged
mesh/check pass e031758e4a37 on 6c1f7cc7f50f | unit 4510 run, 4259 kept, 49 skipped | integration 4589 run, 4361 kept, 51 skipped | over bound: unit, …
The builder claims a done key again as a new revision, so a build kept as done is dropped on any
write of its key. Data items that could not be worked out or written stay due and owe a pass after
the growing bound, so a first write refused before the state or grant exists is tried again
without waiting for an unrelated composition.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 00:27:29 +01:00
jschoubben 122f4dd727 Read a done build once, and work out the data items in the scope the pass composed in (ADR 0577)
The data items after a pass re-read each placed module's build over the bus (about 55 per pass on
ace, g14 and shanks); a done build never changes, so the composer keeps it. The items are now
worked out with the pass's own kept builds and modules run inside the node's own, as composed.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 00:18:30 +01:00
jschoubben e0261cdfc1 Keep each placed module's data items in the engine's state data, so disk-load measures without composing (ADR 0577)
After a pass that handed a new composition, the node's composer works out its modules' data
items from the inputs it composed from and writes each module's key <node>.<module> of
mesh-host_data only when it differs, deleting the keys of modules that left. Pins mesh-sdk
feat/data-items for the contract and the engine's grant. Two tool-answer tests are merged so
the unit count stays where it was.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 00:09:25 +01:00
mesh-tools 6c1f7cc7f5 Merge pull request 'A held module whose backup the node agent interrupted, recorded before #189, is not in the node own composition (issue 1289)' (#190) from fix/held-backup-old-record into main 2026-10-31 19:27:48 +00:00
jschoubben 71afdf3ad2 Keep a have held by an interrupted backup and naming nothing out of the node's own
mesh/delivery merged: source.main moved to novox/mesh-host@6c1f7cc7f50f
mesh/check being carried to 71afdf3ad234 on 6c1f7cc7f50f | from ffa63d25a066
novox's postgres stayed held after mesh-host #189: its have, written before that fix,
names no build, so the node's own composition still took it in and was refused for its
secret reader before the start could back it up again. Only that exact record leaves
the node's own; every other have naming nothing keeps main's rule. Completes #189 for a
have written before it (issue 1289).

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-10-31 20:19:21 +01:00
mesh-tools ffa63d25a0 Merge pull request 'A backup the node agent interrupted runs again, and a held module keeps the build it runs (issue 1289)' (#189) from fix/held-backup into main 2026-10-31 16:33:54 +00:00
jschoubben 76c7ddfc52 Keep main's rule for which haves run inside the node's own composition
mesh/delivery merged: source.main moved to novox/mesh-host@ffa63d25a066
mesh/check being carried to 76c7ddfc5227 on ffa63d25a066 | from a4713e2d28d7
Review round 1: a have naming no build in a phase but running is also written for a
module that runs inside the node's own (waiting for a yes), so taking it out would
drop its resources before the yes. The fp the held have now keeps already fixes
postgres (issue 1289); a test drives an interrupted backup end to end.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-10-31 16:38:05 +01:00
jschoubben 39365c62c9 Resume a backup the node agent's own stop interrupted, and keep what a held module runs
On novox (2026-10-31) postgres's repeat backup was interrupted by the node-engine's
restart: its held have named no build, so the node's own composition took postgres in
and was refused for its secret reader, and nothing ran the backup again. Issue 1289
(R4c, its delivery held).

- A held have names what runs: read from the module's have before backing-up when
  the agent has no running entry, as the not-composed mark does.
- A have in any phase but running that names nothing is not run inside the node's own.
- A backup interrupted by the agent's stop is no failure: the next start backs the
  module up again and applies while the want asks a backup. A real failure stays
  held for the operator's answer.

Quick fix: a new backup ask by hand. Thorough, chosen: the fault fixed where it lives
in the node agent, so every data module's interrupted backup resumes by itself.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-10-31 16:33:49 +01:00
mesh-tools a4713e2d28 Merge pull request 'A node reads its placement per node and module and hands a module as the vault judged (issue 774)' (#188) from feat/072-moves into main 2026-10-31 10:31:14 +00:00
jschoubben 8bdfddab4a Hand a module exactly when the vault's own answer judged it handed, so the node never composes a secret the vault leaves to the provisioner (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@a4713e2d28d7
mesh/delivery-group group feat/072-moves merged: every member is merged
mesh/check failed: shanks: the check of novox/mesh-controller#487 failed: go mod download in modules/route-proxy: go: git.novox.be/novox/mesh-controll…
2026-10-31 09:49:14 +01:00
jschoubben a4c0cee251 Read this node's placement from its node and module keys, or its placement.<node> before the holder converts it, so a write of the holder's is composed only once its node key lands (issue 774, research 072 row 37) 2026-10-31 09:37:23 +01:00
mesh-tools bb8383b065 Merge pull request 'The node composes again only when what it composes from changed (issue 774)' (#187) from fix/compose-only-on-change into main 2026-10-31 01:07:23 +00:00
jschoubben da0f8f1cfd The node composes again only when what it composes from changed, so a want, placement, module or value written again asks the vault nothing (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@bb8383b065a1
mesh/check being carried to da0f8f1cfdb4 on bb8383b065a1 | from 62b211bb6040
Every put under want.<node>.>, every module's take-in and every placement write
made a pass, and each pass asked the vault for the node's own secrets. The
shadow's change check moves onto the composer, gains the want (by its
fingerprint, the only part composed) and serves both loops.
2026-10-31 01:59:58 +01:00
mesh-tools 62b211bb60 Merge pull request 'A record a module once left in the node's composition goes with that module, so the node's own apply is no longer refused (issue 774)' (#186) from fix/folded-record-goes-with-its-module into main 2026-10-31 00:21:40 +00:00
mesh-tools 3d1465606b Merge pull request 'A node says when it composes again, says what a long pass waits on, and composes on request (issue 774)' (#185) from fix/compose-retries-transient into main 2026-10-31 00:21:33 +00:00
jschoubben 1d8d726758 Leave a record the node's own carried for a module that composes its own, and let that module's apply remove it, so the node's own is not refused for ever (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@62b211bb6040
mesh/check checking 1d8d7267584e on 62b211bb6040
On the bus node restic's manifest replaced its process measure by awake (mesh-catalog 35ff67bd). The record of restic.measure was the node's, made while restic was folded, so restic's own apply never saw it, and the guard against removing a placed module's resources refused the node's own apply whole every two minutes from 2026-10-30 19:42: nothing of the node's own applied.

Quick fix considered: forget the one record by hand (a hand act on the node; refused). Thorough fix (this): the node's own apply keeps such a record and says so, never refusing or removing it; a module's own apply owns a node record whose id names it, so it alone removes it once neither composition declares it.
2026-10-31 01:13:11 +01:00
jschoubben b0d6684d37 Retry only a failed apply that is not held, answer a repeated compose from the last pass, and say the try the refusal named (issue 774, review round 1)
mesh/delivery merged: source.main moved to novox/mesh-host@3d1465606bed
mesh/delivery-group group fix/compose-retries-transient merged: every member is merged
mesh/check being carried to b0d6684d3731 on 91fb323ce0cf | from mesh-sdk dec3bfe7a8cb | novox/mesh-host#185 in group fix/compose-retries-transient
The verb compose has no warrant (ADR 0563 §2): Agent.Retry released a module held for a failed backup and re-applied
one already running its want. RetryFailed tries again only a failed apply not held, and refuses in words otherwise.
A hand ask no longer restarts the growing bound, and a pass less than askedWithin old is the answer, so a caller in a
loop drives no passes against the vault. A composer whose loop ended refuses at once. The retry line said try N+1.
The test's first shadow now ends before slowAfter is set, a race the race detector found under the new timing.
2026-10-31 01:08:57 +01:00
jschoubben 56eb0b792c Pin mesh-sdk dfd5d82, so the node-engine announces the verb compose (issue 774) 2026-10-31 00:57:53 +01:00
jschoubben 9c15610f70 Say when the node composes again after a seat that did not answer, say what a long pass waits on, and serve the verb compose (issue 774)
On 2026-10-31 ace's route-proxy said only that the vault could not be asked, and ace said nothing for five minutes
while a pass waited after the vault restarted; the retry that already existed was invisible. A refusal for a seat or
a read that did not answer now says when the node composes again ("the node composes again at HH:MM:SS (try N)"), a
retry pass says it starts, a pass that runs on says every minute what it waits on, and the node-engine's verb
compose makes a pass now and says how it went, so a person recovers without a restart.
2026-10-31 00:56:47 +01:00
mesh-tools 91fb323ce0 Merge pull request 'A held module keeps what it contributes, a have stuck at waiting-yes clears, and the node-engine shows a composition (issue 774)' (#184) from fix/held-keeps-contributions into main 2026-10-30 23:52:52 +00:00
jschoubben 8da56ead81 Pin mesh-sdk 8ed3b87, so a held module's ungranted contribution never puts a stand-in in its provider (issue 774, review round 1)
mesh/delivery merged: source.main moved to novox/mesh-host@91fb323ce0cf
mesh/delivery-group group fix/held-keeps-contributions merged: every member is merged
mesh/check checking 8da56ead8199 on 91fb323ce0cf | novox/mesh-host#184 in group fix/held-keeps-contributions
2026-10-31 00:36:17 +01:00
jschoubben 7bbba2e39e Keep a have's sealed values only when they are of what runs, when a waiting-yes have says running again (issue 774, review round 1) 2026-10-31 00:35:22 +01:00
jschoubben 08e9d15817 Serve the node-engine verb composition: a module's composition beside what runs, its judgement and its diff (issue 774)
Four providers waited for a yes on 2026-10-31 and nobody could read what waited: the have said only
"where its data is kept". The read verb composition answers it from the engine: the latest revision of the
want's build or the one asked by digest, every revision the bucket keeps, what runs and where that is known from,
the want and the have, the move's judgement and whether it waits for a yes, and with diff the resources that
differ, a received file naming its consumers. revisionNamed now reads the same revisions.
2026-10-31 00:26:57 +01:00
jschoubben 1b624c3ec7 Pin mesh-sdk 6bce1fb, so a held module keeps its contributions and the seat's contract names the verb composition (issue 774) 2026-10-31 00:24:56 +01:00
jschoubben 7e2d947f5f Say running again when a provider's composition goes back to what runs while it waited for a yes (issue 774)
waitsForYes returned at once for a composition equal to what runs, and settleOne then found nothing to apply, so
the have kept phase waiting-yes for a move that no longer existed (ace.postgres, novox.postgres, novox.keycloak and
novox.mongodb on 2026-10-31). The have is now rewritten to running.

Quick fix: rewrite the have where the equal composition is met. Thorough fix (not here): one place that writes a
module's have from the settled state after every settle, so no early return can leave a stale phase.
2026-10-31 00:20:43 +01:00
mesh-tools 1e833feb39 Merge pull request 'Pin mesh-sdk eff81f1, so a held module never refuses the node (issue 774)' (#183) from fix/held-module-and-assigner-contract into main 2026-10-30 21:17:39 +00:00
jschoubben 62cfb45789 Pin mesh-sdk eff81f1, so a module held for a missing secret never refuses the node's whole composition (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@1e833feb3940
mesh/delivery-group group fix/held-module-and-assigner-contract merged: every member is merged
mesh/check checking 62cfb45789c1 on 1e833feb3940 | novox/mesh-host#183 in group fix/held-module-and-assigner-contract
2026-10-30 21:21:19 +01:00
mesh-tools 16b8f591ed Merge pull request 'Keep the kept declaration read while a module runs at an old-form build, so novox and shanks apply their own composition again (issue 774)' (#182) from fix/kept-read-and-foreseen-account into main 2026-10-30 19:49:17 +00:00
jschoubben 15361c661f Pin mesh-sdk 4e9621ed, so a proposal foresees a bus account the Approve will mint and a held module's first reason leads a refusal (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@16b8f591ed6d
mesh/delivery-group group fix/kept-read-and-foreseen-account merged: every member is merged
mesh/check pass 15361c661f51 on b133a8949dcb | unit 2719 run, 602 kept, 36 skipped | integration 2731 run, 645 kept, 29 skipped | over bound: unit, in…
2026-10-30 20:34:12 +01:00