Commit Graph
1042 Commits
Author SHA1 Message Date
jschoubben ec3a65fa8d Merge branch 'b6/registry' into feat/registry-milestone 2026-11-05 15:12:02 +01:00
jschoubben 5906e18028 Node-engine: a composition, its codec, digest and keys come from the node-engine client; the candidate answer and its limits from the seat's contract; the declared packages are the engine's own (H-2, H-3, ADR 0634 §1; issue 1966)
The engine's Composed keeps the catalogue's Plan and the declaration types and goes through nodeengineclient's codec; the fingerprint key part is meshcatalogclient's. The candidate answer is nodeengine.CandidateAnswer; the have still holds it through an interim JSON bridge (link.interimAnswers) until the Have group moves.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 15:10:45 +01:00
jschoubben 0871fa7cc4 Node-engine: the value version comes from mesh-sdk go/keys, not go/delivery (H-2, ADR 0634 §1, §4; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:59:39 +01:00
jschoubben cdd428a7d8 Node-engine: the whole seat-list case proves a fresh node asks the seat over its own grant while its own composition and web are held, no part needs letting through (ruling 15:1x b, ADR 0636 §1; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:57:50 +01:00
jschoubben 514ffc30da Merge branch 'b6/registry' into feat/registry-milestone 2026-11-05 14:54:25 +01:00
jschoubben df44d21c82 Node-engine: composition comparison, seals and differing paths are the engine's own package, internal/compositions, with the tests moved from mesh-sdk's delivery (H-1, ADR 0634 §1; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:53:06 +01:00
jschoubben dcbdc70919 Node composer: while the seat list is unread the node's own composition is held too, whole, under the scope node; only what runs and the bootstrapped modules go on (ADR 0636 §1; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:48:20 +01:00
jschoubben 84e50beb65 Node-engine: the private network's names and generator come from mesh-wireguard's overlay package; the host's internal/overlay is deleted, mesh-sdk's go/overlay has no importer left here; contracts imports follow the domain folders (ADR 0636 §6, ADR 0634; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:45:43 +01:00
jschoubben 308dbb94b8 Node-engine: seat-list and seat-list-again answer every operator's session and the healers, no other caller (ADR 0636 §7; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:45:43 +01:00
jschoubben c642471c16 Node-engine: the first read of the seat list is awaited on the reader's Ready, not on Start's return, and its end is told to the waiter and the composer (B9c, ADR 0636 §1; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 14:45:43 +01:00
jschoubben 24cfc18383 Merge b6/registry into the registry milestone's join (issue 1966) 2026-11-05 14:39:37 +01:00
jschoubben f29b19aff2 Node-engine manifest: invokes the seat mesh-catalog's seat-definitions, consumes its two events, keeps the state findings (H-7, ADR 0636; issue 1966) 2026-11-05 14:30:08 +01:00
jschoubben 855f4471e2 Node-engine: reads the seat list through the seat mesh-catalog, holds what is new until it was read, writes its finding after five minutes; verbs seat-list and seat-list-again; whole-module case against a stand-in of the seat (H-7, ADR 0636 §1, §7, §8; issue 1966) 2026-11-05 14:26:15 +01:00
jschoubben 6ebad21cbe Node-engine: the seats are called by their names mesh-module-assigner and mesh-node-enroller (ADR 0634 §9; H-5; issue 1966) 2026-11-05 14:17:46 +01:00
jschoubben a222db8c69 Node-engine composer: a module new to the node is held until the seat list was read, unless its seats are bootstrapped; the read hands it (H-7, ADR 0636 §1; issue 1966) 2026-11-05 14:17:28 +01:00
jschoubben 66fe7c8a0d Node-engine: the finding of a seat list unread for five minutes, written once and cleared at the first full read (H-7, ADR 0636 §1; issue 1966) 2026-11-05 14:16:04 +01:00
jschoubben 7623279efe Node-engine: no compiled seat list; the seat's name and verbs come from its contract, the tests give catalogue their seats (H-7, ADR 0635 §1; issue 1966) 2026-11-05 14:14:46 +01:00
jschoubben 570b66230c Composer: find the store by the holder of any of its names, the old seat's too (ADR 0632 §2; issue 1966)
While the indexer holds distribution's new manifest, every node reads main's,
which claims only mesh-artifact-store: a new engine must find the store there.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 21:29:12 +01:00
jschoubben e5d9753368 Resolution test: the runtime's trust is filled from every name of the store (ADR 0632 §1; issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 21:10:55 +01:00
jschoubben 6adaf44f65 Composer: the store's address is the image seat holder's listen open to the mesh (ADR 0632 §1; issue 1966)
The provision artifact-store retires, so the store is found by the holder of
container-image-registry and the listen it opens to the mesh, on the port its
node put it on; every name of the store, oci-artifact-registry's too, answers
that address. The engine states form:seat-reach with its contract.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 20:56:43 +01:00
jschoubben 66e89b71a2 Offer the catalogue forge's provisions in the toolchain test's stand-in (issue 1966)
The group's mesh-build-go requires go-package-registry, which the forge
now provides; the stand-in listed the forge's offers by hand and missed it,
so the test failed against the group's catalogue. It now takes them from
the catalogue's gitea manifest, so a registry the forge gains needs no edit.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 19:56:18 +01:00
jschoubben 32c5df952c Pin mesh-sdk 05b3e2e1b94f and mesh-contracts ae5266e77639, the group's final heads (issue 1966)
mesh/delivery ready: it is merged on the Approve
mesh/check failed (7): shanks: novox/mesh-catalog#612 does not merge into the trunk: files in conflict: mod…; and 6 more | 32c5df952cbf on e73aae3686a6
mesh/delivery-group group feat/registry-milestone rejected: composed together: the checker's verdict at the members' heads (068b7d23cf1a): shanks
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 18:47:39 +01:00
jschoubben 4363c371c5 Pin mesh-sdk e5d89c5eedb5 and mesh-contracts 347a3053580a, the group's heads after review round 2 (issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 18:26:33 +01:00
jschoubben 1b01d065ac Pin mesh-sdk c54648e74346 and mesh-contracts ec9529022c44, the group's heads (issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 17:46:08 +01:00
jschoubben d3ee49d6f3 F-host 2, ENG-5: on a fresh mesh the bootstrap pushes the holders' own images to the store on its loopback with no credential, and names them under the seat's reach (lead's design limit, review of issue 1966)
The store's front is distribution's program, a bundle the mesh builds, so nothing serves it before the builder: the
bootstrap's /v2/ wait and its pushes now go to the loopback port the store's manifest publishes, as the publish contract
allows on the store's own node. The one-off credential and its file go: the runtime could not carry it on a push, and no
push needs it. The tags list is asked for the repository being published, not always the control plane's.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:29:56 +01:00
jschoubben ded92a8137 F-host 3: the agent's tests of a pull waiting on its holder wait for the loop to come to rest, not 50-100 ms, before they count applies (review of issue 1966)
waitIdle returns once every change sent to the loop is taken in and applied and no backoff wait runs, so a retry the
clock or a wrong settled would start is counted, with no real sleep.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:26:16 +01:00
jschoubben 20614822df F-host 2, ENG-5: the bootstrap's one-off push credential goes to distribution's account, mode 0600, only once the node-engine made that account and its state directory, so the holder's program can read it (review of issue 1966)
The owner is the account the manifest names for the directory that holds the file; the bootstrap no longer makes that
directory itself, and a run that reaches it first is refused naming what it waits on.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:24:14 +01:00
jschoubben 9740238ec9 F-host 1: ignore the binaries go build leaves in cmd/, so a built node-engine is never committed again (review of issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:20:46 +01:00
jschoubben a2313057c0 ENG-9: a failed have carries the pull it waits on, read through node_haves
The image, the seat and its holder, when the pull began, and the last retry with what triggered it ride in the failed
have's pull-wait (mesh-sdk delivery.Have.PullWait, asked of the lead in j1-sdk-requests.patch), from the agent through
the link's module fact to the have bucket; the agent's own read view of them goes with the verb. Rows in the agent's
pull test and the have bucket's test; each failed without the code.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 14:02:44 +01:00
jschoubben 2b14f166de The node-engine speaks operator-channel, mesh-vault and node-engine through their contracts and clients; it serves no pull-waits
ADR 0627 §1, §4: mesh-sdk keeps no asks, asker, vault answers or compose-candidate checks, so the node-engine takes them
from mesh-contracts: operator-channel's contract and client (asks, warrants, the asker's client), mesh-vault's sign verb
and answer, and node-engine's own contract and client for compose-candidate's ask, answer, verdicts and bound. The
lead's ruling on pull-waits (no record gives the seat that verb): the verb and its handler go; the row that read the
waits now holds the seat refusing the verb, and failed before the change. The go.mod requires of mesh-contracts' modules
come with the group's pin.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 14:02:44 +01:00
jschoubben d8207358b0 Join mesh-host's main (issue 1585's held-answer fix, #208) into the registry milestone
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 13:46:50 +01:00
jschoubben c6ae0fc405 The node-engine answers compose-candidate in its seat's contract: the input as a JSON object, a module's fingerprint Fp
mesh-sdk's ComposeCandidateAnswer and ModuleComposed are now the generated nodeengine types (ADR 0613 §3, §5), so the
input sent for the checker's sandbox is decoded once into the object the contract carries, its numbers kept as written
(json.Number), and its size still counted from the bytes it was marshalled to.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 12:40:34 +01:00
jschoubben 0128b962ab The node-engine speaks its seat's generated contract: manifest-reads and manifest-reads-changed in mesh-contracts' types (ADR 0618 §3, §4, ADR 0613 §3)
The verb's answer and the event's body are mesh-contracts' nodeengine types, not mesh-sdk's, and the seat's verb and event
names are the contract's. The two reasons of the event, started and returned, are the engine's own words beside it.
Requires mesh-contracts and the registry milestone's mesh-sdk (a go.work, uncommitted, until the group pins them).

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 12:38:50 +01:00
jschoubben 271de0108d The node-engine serves manifest-reads and says manifest-reads-changed once per link (ADR 0618 §3, §4, issue 774)
Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 12:38:30 +01:00
jschoubben c93feeb30f ENG-9: the node seat's read verb pull-waits answers which failed applies wait on which store's holder and the last retry with its trigger (design 51, issue 1966; needs the verb in the node-engine seat's definition)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:15:26 +01:00
jschoubben 7f33c8b499 ENG-3, ENG-4: the node-engine keeps no seat table of its own (mesh-contracts' list through mesh-sdk), answers ${seat:...:reach} for both names of the store, and reads both reference forms in composition, image seat and bootstrap (ADR 0605 §9, design 26, issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:11:34 +01:00
jschoubben 18c1735f7c ENG-5: the bootstrap makes a one-off push credential for the holders' first images, writes it for the store's holder and forgets it when its run ends (design 51, issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:07:34 +01:00
jschoubben 2a71a8ff76 ENG-8, ENG-9, ENG-11: a failed pull from a store is tried again once per settled of the store's holder, heard on every node, and the failed have says whom it waits on and what triggered the last retry (design 51, issue 1966; needs mesh-sdk request R1, R2)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:49:44 +01:00
mesh-tools e73aae3686 Merge pull request 'Node-engine: act on a backup answer whatever runs, say why a module stays held, and show the want and the hold in the composition verb (issue 1585)' (#208) from fix/1585-held-answer into main 2026-11-04 10:47:20 +00:00
jschoubben 3eef84d979 ENG-10: the composition writes beside each routed image the seat it came through and that seat's holder, no part of a container's spec (design 51, issue 1966; needs mesh-sdk request R1)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:46:33 +01:00
jschoubben 60358d01e9 ENG-note: notHandedWhy's impossible branch says it is a fault in the node-engine's composition (review of issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:43:46 +01:00
jschoubben 05418b4fe5 ENG-7: every new image of a module of two containers or more is pulled before any of its containers is set aside, and a pull that fails replaces nothing (design 51, issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:43:17 +01:00
jschoubben 2779f5312b ENG-2: the node's record says the capabilities its tool runner states of its calls, asked of the runner's PING at each report (issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:41:11 +01:00
jschoubben e2380404a1 Act on a backup answer whatever runs, say why a module stays held, and let the engine's read tool show its want and its hold (issue 1585)
mesh/delivery merged: source.main moved to novox/mesh-host@e73aae3686a6
mesh/check being carried to e2380404a1fa on e73aae3686a6 | from 998d8f737008
nats on novox, 2026-11-04 11:07: after mesh-host #207 the module stayed held
although its want carried "Back up again", and the engine said nothing. The
engine's composition verb showed it running what its want names (taken from
its held have at the start), so when the answer was in a revision of the want
read after the hold, settleOne returned on "it runs what the want names"
before it looked at the hold: the answer was swallowed, silently. The start
path #207 fixed was right but never met; no test ran the live shape.

- settleOne: the hold is looked at before what runs. An answer (again or
  skip) in a want revision after the one the hold took is acted on whatever
  runs; with no answer the hold is kept and said with both revisions.
- baseline: the hold kept at the start is said (it was the silent branch).
- composition verb: the want as the engine holds it now carries self, backup,
  words and its key's revision, beside the engine's attempt (held, answered
  revision, want and composition revisions, transient) and whether the module
  was looked at since the start, so which want the engine read is read, never
  guessed.

Quick fix: answer once more by hand (a new want revision), which the guard
still swallows while what runs equals the want, so none exists. Thorough fix,
chosen: the order of the guards and the read tool, proved by rows of the live
shape (a self module of policy record, its held have naming the want's own
composition, the answer before the start, and again and skip arriving while
held); the two later rows fail with the old guard order.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 11:36:14 +01:00
mesh-tools 998d8f7370 Merge pull request 'Node-engine: a stop never cuts a save off, an archive's unpack is recorded at once, and an answer the want carries is acted on at start (issue 1585)' (#207) from fix/archive-record-at-unpack into main 2026-11-04 10:04:25 +00:00
jschoubben 5dc3a8e17f Keep what an archive unpacked in the engine's own state directory, never beside the archive, and stop no slower than the host waits (issue 1585, review round 1)
mesh/delivery merged: source.main moved to novox/mesh-host@998d8f737008
mesh/check being carried to 5dc3a8e17f97 on 998d8f737008 | from fabfdf04f78a
The journal of an unpack sat at <target>.unpacked.json, in a directory
the module's account may write: a forged one would make the root engine
remove or adopt any path it named. It now lives under
<statedir>/unpacked/<sha256 of the target>.json, mode 0600, names its
target, and is refused whole when an entry is not filepath.IsLocal or a
parent is not above the target. It goes when the archive is taken off,
whether its state record still said what it unpacked or not.

The launcher's stop grace for a host on trial rises from 20 s to 75 s and
the unit's TimeoutStopSec is 120 s (openrc the same), past the host's own
60 s wait for the apply in flight, so neither cuts a save off again.

Tests folded as rows into existing ones (the repository is over its unit
budget): the journal's rows, a forged entry and parent refused, the
journal gone with a record that lost its Unpacked; the test that passed
on main dropped; the stop test waits on its own line, not a sleep, and no
longer consumes the save it waits for.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 10:52:42 +01:00
jschoubben 768f637bd0 Act at the engine's start on a backup answer the want already carries, so an answer written before a restart is never swallowed (issue 1585)
nats on novox, 2026-11-04: the operator answered "Back up again", the
engine restarted, and baseline() took the hold with AnsweredRev = the
want's current revision, so the answer in that very revision was never
acted on and compose refused "held for a failed backup".

Quick fix: answer once more (a new want revision). Thorough fix, chosen:
at baseline, a held module whose want carries again or skip is applied
(again backs it up first, skip goes on without one); a hold with no
answer stays held. The cost said in the code: an answer already acted on
before a stop, whose backup failed again, is tried once more per start.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 10:36:48 +01:00
jschoubben ee0f46b78f Stop between applies and save the node's state as one writer, so a stop never cuts a save off and loses its records (issue 1585)
On novox the node-engine's state had lost the records of nats's two
archives, which only a temporary save file of 2026-10-24 still held, and
about 260 such files (~80 MB) lay beside it. The cause: an apply runs
detached from the engine's stop (applyContext, WithoutCancel), and
runLink returned, and the process ended, without waiting for it; a save
cut between its write and its rename left its temporary file and the
state before it, and whatever the apply did was never recorded.

Quick fix: remove the leftover files by hand and restore the records.
Thorough fix, chosen: the engine ends between applies whichever way it
ends (a stop, standing aside, a move to another bus): it waits up to 60 s
for the apply in flight, then lets no save start (store.Quiesce). Save is
one writer per process, flushes the directory after its rename, and says
loudly each record the state saved before held that this save drops and
nothing forgot (Forget, Release mark what is dropped on purpose, and no
longer write into an array an earlier copy reads). The engine's start
removes leftover temporary saves under the apply lock and says how many.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 10:36:48 +01:00
jschoubben 84a14b08d5 Write down what an archive unpacked as soon as it is unpacked, so a lost record never makes the host's own files foreign (issue 1585)
Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
2026-11-04 10:26:43 +01:00
mesh-tools fabfdf04f7 Merge pull request 'Node-engine: hand a moved module by its manifest, its build and the machine, never by the vault's reading of its plan (issue 1966)' (#206) from fix/handed-one-rule into main 2026-11-04 09:16:13 +00:00