Commit Graph
2092 Commits
Author SHA1 Message Date
jschoubben 08d38f651b Merge branch 'feat/47-G-fix11-b5j' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-assigner/internal/assigngraph/graph_test.go
2026-11-12 10:37:21 +01:00
jschoubben 4e67d7e99f Merge branch 'feat/47-G-fix11-b5i' into feat/47-G-join11 2026-11-12 10:37:09 +01:00
jschoubben 967b636b74 Merge branch 'feat/47-G-fix11-b4e' into feat/47-G-join11
# Conflicts:
#	modules/mesh-grants/internal/buscompose/users.go
2026-11-12 10:37:09 +01:00
jschoubben 644aa8b46c Merge branch 'feat/47-G-fix11-b4b' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/graphkept.go
2026-11-12 10:36:56 +01:00
jschoubben 9fae9009c5 Merge branch 'feat/47-G-fix11-b3d' into feat/47-G-join11 2026-11-12 10:36:38 +01:00
jschoubben 9433140931 Merge branch 'feat/47-G-fix11-b2k' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go
#	modules/mesh-delivery-assigner/manifest_test.go
#	modules/mesh-delivery-assigner/module.json
2026-11-12 10:36:38 +01:00
jschoubben 4c2d8f034c Merge branch 'feat/47-G-fix11-b2l' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/buildended.go
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/graphkept.go
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass.go
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/tools.go
#	modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/world.go
#	modules/mesh-delivery-assigner/internal/assign/wants_test.go
#	modules/mesh-delivery-assigner/manifest_test.go
#	modules/mesh-delivery-assigner/module.json
2026-11-12 10:36:04 +01:00
jschoubben c28d12330e Merge branch 'feat/47-G-fix11-b2f' into feat/47-G-join11 2026-11-12 10:32:18 +01:00
jschoubben d89a6bacc6 Merge branch 'feat/47-G-fix11-b1g' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-contracts/seats/mesh-pipeline-runner/seat.json
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/main.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/steps.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/verbs.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/verbs_test.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/views.go
#	modules/mesh-delivery-pipeline/internal/pipeline/start.go
#	modules/mesh-delivery-pipeline/manifest_test.go
#	modules/mesh-delivery-pipeline/module.json
2026-11-12 10:32:18 +01:00
jschoubben 5db642e161 Merge branch 'feat/47-G-fix11-b1f' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline-tools/main.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline-tools/main_test.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/findingwriter.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/main.go
#	modules/mesh-delivery-pipeline/cmd/mesh-delivery-pipeline/views.go
#	modules/mesh-delivery-pipeline/go.mod
#	modules/mesh-delivery-pipeline/internal/pipeline/read_test.go
#	modules/mesh-delivery-pipeline/internal/pipeline/start.go
#	modules/mesh-delivery-pipeline/manifest_test.go
#	modules/mesh-delivery-pipeline/module.json
2026-11-12 10:30:22 +01:00
jschoubben 4aaa805f2a Merge branch 'feat/47-G-fix11-b1e' into feat/47-G-join11 2026-11-12 10:28:34 +01:00
jschoubben 2bb1b273fe Merge branch 'feat/47-G-fix11-b6b' into feat/47-G-join11 2026-11-12 10:28:34 +01:00
jschoubben 2551d13cf3 Merge branch 'feat/47-G-fix11-b7' into feat/47-G-join11
# Conflicts:
#	.gitignore
2026-11-12 10:28:26 +01:00
jschoubben 1e00430eef Merge remote-tracking branch 'origin/main' into feat/47-G-join11
# Conflicts:
#	modules/mesh-delivery-indexer/judge/rules.go
#	modules/mesh-delivery-indexer/judge/rules_test.go
2026-11-12 10:28:05 +01:00
jschoubben cfa02ea26e Join 11: refuse a committed program anywhere in the repository
A compiled program reached a commit a second time (the view's 18 MB program at
its module's root, which the command-folder pattern did not cover): the sdk's
nobinaries test refuses every tracked executable, as the catalogue's
checks/nobinaries does, and .gitignore covers a module's root.

Builds: design 47 G join (the kind's guard, fix-a-bug-met-twice)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 10:22:17 +01:00
jschoubben b58b3a9f6a AS5-5: the whole composition step against a stand-in node-engine and Go build seat
The assigner started whole takes the runner's composition step of a module on three machines, one away: it asks the two others'
engines for their inputs, puts one run ask per machine under its name, the stand-in build seat reads each request by mesh-wants'
input in chunks and says its step-ended with the composer's verdict, and the step's end fails naming the machine that does not
compose, warns of the one away, and keeps no input. The assigner says in its journal when it serves the step and when a machine
goes away or comes back.

Builds: design 47 G §354, §22, §63, §64, §389, A-240, A-252, A-254

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:57:26 +01:00
jschoubben 0d98108bc4 AS5-4: a running composition step says step-progress once a minute
A ticker inside the running step, stopped at its end, says the seat mesh-wants' step-progress.<node> with the ask, the asker
runner, the phase the run is in (fetch while its revision and inputs are read, run while its run asks run, report) and the time.

Builds: design 47 G §496, §603, A-262, A-267

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:54:12 +01:00
jschoubben 4f7206702f AS5-3: the composition step runs one composer request per machine as a run ask and reads its verdict from step-ended
Each machine placing the module on the step's branch is asked compose-candidate for its input; one composer.Request per machine
(depth compose, the one machine, one case change) carries it with the manifests, verdicts and seat definitions of the step's
catalog revision read through mesh-catalog's revision; the request is kept under its run ask (refused at the inputs state's
64 MiB cap, naming the store); one run ask per machine on node-build-go's check queue under the assigner's name, naming no head,
its program the node-engine build's catalog entry (the build each machine runs as its running says, read through entry, or the
revision's where it differs from the trunk's) with the composer's package. The run's end is heard on the build seat's step-ended
and its verdict read through buildstep.StepEnded.ReadOutput; a machine the watchman said away passes with a warning. The step
is served from main.

Builds: design 47 G §354, §22, §63, §64, §389, A-226, A-238, A-240, A-243, A-252, A-254, A-261

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:52:14 +01:00
jschoubben cd51fec81a B2l AS17 (4): §298's three order edges are acting waits of the assigner under order.
The new node-backup and bus module holders run before a module whose kept data's form changes moves; at a revert the node-engine goes
back last on its node; the reverted assigner and gate are wanted only once take-in.taken names the trunk's head (A-260). Kept in the
state waits, said as wait-held and wait-ended, read and released by assigner_waits and assigner_release. The manifest consumes the
indexer's take-in.taken and its version is 0.3 (A-245).

Builds: design 47 G §298, A-245, A-258, A-259, A-260
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:49:25 +01:00
jschoubben 12c213eb4c Merge commit 'b4eb78c2' into feat/47-G-fix11-b2k 2026-11-12 09:47:17 +01:00
mesh-tools bf36f84445 Merge pull request 'The second reader release: the validator judges G's new forms (design 47 G §621, §697, §508)' (#205) from fix/47-G-reader-release-2 into main 2026-11-12 08:41:41 +00:00
jschoubben 5094abded9 C1c-3: the delivery SDK gives askers one reader of a run's answer from a step-ended
StepEnded.ReadOutput takes the inline `output`, else asks the seat's verb output on the node that ran the ask for the object
`output-object` names (the runtime hands the chunks as the member `file`, A-249); a step-ended with both is refused.

Builds: design 47 G A-254, A-240, A-247

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:40:36 +01:00
jschoubben 7125a9f92f C1c-2a: the step runner says a run's answer in step-ended, inline or as an object, and serves it
The puller's end of a run carries the run's program's last line of standard output as `output` up to StepEndedInlineMost, else keeps
it with KeepOutput and names the object in `output-object`; a program that exits other than 0 ends failed. OutputTool serves the
seat's verb `output` from the kept object, as the whole file the tool runner sends in chunks; OutputsStore names the store.

Builds: design 47 G A-254, A-257, §602

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:37:51 +01:00
jschoubben f06c9cf5fd C1c-1: the three build seats' verb output answers a run's answer in chunks
The part build-step gains the verb `output` (the ask and the call in, the ask and the chunk count out, marked chunks, read by the
askers of a run), and the fields output and output-object of step-ended name the run's answer inline or as an object the holder
keeps. The three seats' fixtures carry the verb; the generated Go and TypeScript follow.

Builds: design 47 G A-254, A-139, §712, §354

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:34:48 +01:00
jschoubben 1e48d04077 B2h AS17 (3): the pass asks about a held revert, and writes live into the want
A have held with restore point missing or damaged, for a want the
assignment graph tells a revert (Reverted) across a kept data's form
(FormChanges of the running entry and the want's, read through the catalog
seat's verb entry), gets the revert ask and the finding revert-held; backups
leaves it. Live writes the want with revert live and its words, hold writes
nothing; the finding clears once the reverted build runs, and a newer want
takes the ask and the finding away.

Builds: design 47 G §710, §300, §298; A-217

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:34:13 +01:00
jschoubben 0930d57546 AS5-2b: the composition step's machines and their inputs, asked by compose-candidate and kept
The machines of a step are the nodes whose placement names its branch (none named is the trunk; an unwatched branch takes the
trunk's nodes); each is asked compose-candidate with the step's candidate key (run-<id>, or <branch>.<revision>) and input true,
and its input is kept under the ask named for it. Not yet called by a run (AS5-3).

Builds: design 47 G §354, §22, A-226, A-243, A-252

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:33:52 +01:00
jschoubben 103174e44c FB2: the forge bridge says the finding branch-unprotected
A move of a watched branch whose protection was removed writes nothing and is said as the
finding branch-unprotected (scope merge, id the repository and branch as a key, warning) through
mesh-sdk's held finding writer into the bound state findings; a move that finds the protection
back clears it. The findings state is bound by the Binding with performed.

Builds: design 47 G §17, §131, §387, A-251
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:32:36 +01:00
jschoubben 36b893c523 AS5-2a: the assigner keeps a step's input of any size and answers the verb input in chunks
The seat mesh-wants' verb input is marked chunks (call in, chunks out); the program answers it with the sdk's SeatChunks.Send from
the state inputs and its objects store, deletes an input at the build seat's step-ended of its ask or a day after its write, and
assigner_inputs reads them. The verb is no longer a tool. Mirrors the indexer's run_inputs.go (owed to mesh-sdk).

Builds: design 47 G §354, §712, §681, A-237, A-243, A-139

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:32:03 +01:00
jschoubben 6cbaa84c52 Merge branch 'feat/47-G-fix11-c1b' into feat/47-G-fix11-c1c 2026-11-12 09:31:16 +01:00
jschoubben 86af4c29c4 AS6: assigner_delivered_again asks a version said delivered again, heard at once in delivered-again
The tool records its hand act, writes the ask {module, version, by, why} into the state
delivered-again and answers that it is asked; the process's watch of that state wakes a pass at once,
which says version-delivered again when every present machine placing the module runs that version
healthy and takes the ask away, else keeps it with why, which assigner_waits lists.

Builds: design 47 G §376, A-250

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:29:57 +01:00
jschoubben fea9de98eb AS6: assigner_release with step delivery releases a dependent's delivery wait; assigner_waits lists them
The hand act is recorded first, then the release is written on the wait in delivery-waits, which the
process ends released at its next pass, writing the dependent's want.

Builds: design 47 G §376, A-246 (G-5's row "delivery of a dependency")

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:27:59 +01:00
jschoubben 87edff67ee B5g5-1: the indexer sends the input's chunks with buscalls.SeatChunks.Send, so its Subscribe exception goes
The acknowledgement subscription is the SDK's; the indexer's inputs.go holds none, and the test that forbids a subscription in
the program no longer excepts it.

Builds: design 47 G §712, §681, A-237, A-139

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:27:01 +01:00
jschoubben 8597083493 AS6: the assigner says version-delivered and holds a dependent for its dependency's delivery
At its start it reads whole, from the first message, version-released, its own version-delivered and
the watchman's machine-away and machine-back (process.FromFirst), then hears them; it reads the
watchman's presence state no more, a machine being present unless said away. A module's newest
released version is said version-delivered once every present machine placing it on its release
branch runs healthy the want naming it, the machines away named, none when every one is away. A
dependent whose want changes fingerprint waits, by an edge of kind 2, 4 or 6, for its dependency's
newest released version on its branch not yet said delivered: wait-held step delivery, bound 60
minutes from the release, kept in the state delivery-waits, ended ran on the delivery or released
once assigner_release writes it.

Builds: design 47 G §376, §389, §498, §374, A-189, A-233, A-246

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:26:49 +01:00
jschoubben c8e1c8b40d AS5-1: the assigner takes the composition step and says its step-ended
The seat mesh-wants' accepted kind step is taken for the composition step and its end said as step-ended.<node>
under the ask's id; it is wired with its run in AS5-3.

Builds: design 47 G §22, §354

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:24:54 +01:00
jschoubben b316489b93 B2h AS17 (2b): the revert ask's words, options and its finding revert-held
RevertAsk asks hold or live, naming the reverted build, the kept data, its
form and the node's words; it expires leaving the newer build running.
RevertHeldFinding is delivery.<node>.<module>.revert-held, a warning that
needs nothing, so the ask is the operator's one message.

Builds: design 47 G §710, §300

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:24:24 +01:00
jschoubben 42c3db1d5b B2h AS17 (2a): the assigner's ask of a held revert has its kind and key
The asks state gains the kind revert, keyed revert.<node>.<module>.<hex>
after the reverted build's fingerprint; like the backup ask it needs no
proof of who answered, so the view may answer it, and an asking that ends
unanswered while the hold stands is written again.

Builds: design 47 G §710, §300

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:24:04 +01:00
jschoubben ad561e485f FB7, FB8: the forge bridge asks its findings state and finding stream in the template form
The manifest's requires gain findings (state, history 5) and finding-events (events, 7 days,
16 MiB) and its consumes mesh-conditions.findings-asked, as every part but a foundation part
declares them; the bridge gains the finding branch-unprotected (next commit).

Builds: design 47 G §387, §529, §383
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:21:51 +01:00
jschoubben 2aed3151af B5g3-3: the indexer keeps an input of any size and answers input in chunks
Builds: design 47 G §712, §354, §681, A-237, A-139

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:20:34 +01:00
jschoubben c4ba9eb23e B2h AS17 (1): a want carries the operator's answer live to a held revert
The seat mesh-wants' want gains `revert`, enum live, beside `backup`; the
wants codec refuses any other answer. The assigner's state form changes, so
its manifest's version moves to 0.2 once (A-245).

Builds: design 47 G §710, §300; A-245

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:19:54 +01:00
jschoubben a600090fcd G-715: the forge bridge says a group's end once per base
ForgeGroupEnd gains base; the bridge reads each member's base from the seat's
verb groups (Member.base is already in the contract and seatgen's output),
keeps groups.<group>.<base>, says one end per base from that base's members,
and bridge_group_end_again takes the base.

Builds: design 47 G §715, A-236
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:19:46 +01:00
jschoubben 06e5980898 VW2: the view's waits are the parts' wait-held, kept in its state waits
The view's process hears wait-held and wait-ended of every part (seats and modules; waits.go, manifest consumes) and keeps them in
its state waits under owner.step.node.module (viewstate/waits.go); view_tiers reads them through the tools bundle and names each
module's waits in the sayer's own words, {owner, step, node, on, bound, since, words} (tiers.go waitsHeld), beside the indexer's
pending revisions as the seat mesh-catalog's step take-in. No state of another module is read for a tier's waits.

Builds: design 47 G §512, §376, §498, A-242
2026-11-12 09:19:18 +01:00
jschoubben 9e4ced8274 B5g3-2: the mesh-catalog seat's input verb answers in chunks
Builds: design 47 G §712, §354, §681, A-237, A-139

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:18:18 +01:00
jschoubben 2879e78481 B5g3-1: a run's input is any size, RunInputBound goes
Builds: design 47 G §712, §354, A-237

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:18:18 +01:00
jschoubben f0c4d564d2 GR16 §554: the recovery module's account is granted the bus names of kept data
The account of the module claiming the seat mesh-recovery is granted the stream snapshot and
restore subjects with their chunk subjects, a stream's deletion and a durable consumer's
creation, only for the bus names of every kept data of every manifest the part holds and of
every restore point or aside kept-points names; never a stream of the mesh's own (CONTROL,
NODES, EVENTS, DEAD_LETTER*, PROV_APPLIED, CHECKS_*, SEAT_*, a seat's KV_). The writers table
names the account as a deleter of kept data's streams (ADR 0672 §4).

Builds: design 47 G §554, §565, A-230

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:17:07 +01:00
jschoubben 9e02d6234b A-238: a run ask whose program reads only its input names no head
The indexer's validation runs its manifest validator over its input alone
(design 47 G §712: heads none); the holder lays an empty MESH_RUN_HEADS.

Builds: design 47 G §712, A-238

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:16:19 +01:00
jschoubben 316af50da3 VW3: the view's tiers are the assigner's difference of a source revision's kept revision
view_tiers compares the kept assignment revision whose catalog carries the source revision with the one kept before it,
through the seat mesh-wants' verbs revision and difference (view/difference.go, tiers.go Tiers), and reads the tiers and each
module's after (module and edge kind) from the answer; the view orders nothing. The manifest invokes the two verbs.

Builds: design 47 G §512, §186, A-242
2026-11-12 09:15:40 +01:00
jschoubben 3cc4cb9392 GR16 §565: the grants part reads the restore points' names through kept-points
The part calls seat:mesh-recovery.kept-points as a kept call at its start and again on the seat's
ended (outcome done), point-removed and expiry-ended, reads points[].names and asides[].names,
keeps them in its state memberships (kept-names.mesh-recovery) and grants the names it kept until
the call first answers; grants_first_views says the wait. A name leaves only by an answer.

Builds: design 47 G §565, §554, A-230

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:15:27 +01:00
jschoubben dce4d01a58 FB1, FB9, FB10: the forge bridge writes one message per move of every watched branch
The bridge watches the registrations' branches by origin (a first view of registrations, then registration-changed),
before the first registration the gate's source's (source-moved); no branch is named in its code, Trunk goes. Every
move is heard on the seat git's branch-moved through the bridge's durable on SEAT_git_branch-moved, its head read by
the seat's verb branch, never the event's: merged with its pull request and `base` (the watched branch) when the
seat's pull names the head a pull request's merge, else `pushed` with branch, head and `before`, both with the
take-in inputs at the head. The last head written per watched branch is kept in the state heads under
branch.<owner>.<name>.<escaped branch>, so a move heard twice writes once, and each watched branch's head is read at
the start and when a branch is registered. A move of an unprotected branch writes nothing. pull.merged into a watched
branch writes nothing (its message is the move's); the forge-made merge's trunk filter goes.

Builds: design 47 G §4, §7, §11, §20, §41, §507, §509, §532, §714, A-227, A-236

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:15:04 +01:00
jschoubben d5676ee4b3 AS12: the assigner keeps a run's candidate assignment graph, dropped on the run's group-ended
A run's candidate catalog-revision (meshcatalogclient's candidates hook) is read whole through the
catalog seat's revision verb; the assignment graph is computed from it in place of its branch's half
and kept apart from trunk's under candidate.<group>.<heads>, the group run-<id>. revision, entry and
difference serve it (a candidate against trunk). The runner's group-ended of the run drops it and
writes no want; the gate's heads-replaced drops nothing; its entries stay while it is kept.

Builds: design 47 G §189, §190, §186, §187, §359, A-201, A-235, A-160

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:14:45 +01:00
jschoubben ccbefc5653 B5g3-2: the mesh-catalog seat's input verb answers in chunks
Builds: design 47 G §712, §354, §681, A-237, A-139

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 09:14:28 +01:00