From bf880e9f7f93152b3b687f204806e89c9bb2f893 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 11:50:54 +0100 Subject: [PATCH] =?UTF-8?q?SD-M2d:=20mesh-delivery-checker=20and=20operato?= =?UTF-8?q?r-channel=20whole=20(ADR=200620=20=C2=A71,=20=C2=A72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checker's verdict and tag-wanted on its own family checker.asks, preview.written on its stream checks; operator-channel written from the messenger's seat entry, decided its only seat event, offered and closed beside it. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- .../fixtures/events.json | 35 ++ seats/mesh-delivery-checker/seat.json | 209 +++++++ seats/operator-channel/fixtures/events.json | 98 +++ seats/operator-channel/seat.json | 560 ++++++++++++++++++ 4 files changed, 902 insertions(+) create mode 100644 seats/mesh-delivery-checker/fixtures/events.json create mode 100644 seats/mesh-delivery-checker/seat.json create mode 100644 seats/operator-channel/fixtures/events.json create mode 100644 seats/operator-channel/seat.json diff --git a/seats/mesh-delivery-checker/fixtures/events.json b/seats/mesh-delivery-checker/fixtures/events.json new file mode 100644 index 0000000..46ef4d8 --- /dev/null +++ b/seats/mesh-delivery-checker/fixtures/events.json @@ -0,0 +1,35 @@ +[ + { + "name": "verdict on the checker's own family", + "of": "holder-event:verdict", + "subject": "mesh.mod.mesh-delivery-checker.event.verdict", + "given": { + "repository": "novox/mesh-contracts", + "pull": 3, + "head": "cccccccccccccccccccccccccccccccccccccccc", + "verdict": "fail", + "out-of-step": [ + "go/seats/meshbroker/contract.go" + ] + } + }, + { + "name": "tag-wanted on the checker's own family", + "of": "holder-event:tag-wanted", + "subject": "mesh.mod.mesh-delivery-checker.event.tag-wanted", + "given": { + "repository": "novox/mesh-sdk", + "pull": 170, + "head": "cccccccccccccccccccccccccccccccccccccccc", + "module": "mesh-sdk", + "package": "go", + "tag": "registry-1966" + } + }, + { + "name": "preview.written on the checker's stream", + "of": "holder-event:preview.written", + "subject": "mesh.mod.mesh-delivery-checker.event.preview.written", + "given": {} + } +] diff --git a/seats/mesh-delivery-checker/seat.json b/seats/mesh-delivery-checker/seat.json new file mode 100644 index 0000000..e671f2e --- /dev/null +++ b/seats/mesh-delivery-checker/seat.json @@ -0,0 +1,209 @@ +{ + "format": 1, + "seat": "mesh-delivery-checker", + "scope": "mesh", + "grain": "whole", + "decision": "novox/hq ADR 0547", + "verbs": [ + { + "name": "try", + "description": "Try a pushed head before its pull request is opened: the checker judges it as it judges a pull request's head, merged onto its trunk's head, its unit and integration levels through the build modules, and owes the forge nothing. Answers the try's id at once; try-read reads where it stands. A try of the same head that waits or runs is the answer to a second ask; at most 4 wait or run at once, mesh-wide.", + "optional": true, + "replaces": [ + "sh merge-check.sh", + "docker run postgres" + ], + "input": { + "properties": { + "head": { + "description": "the full commit to try, 40 or 64 hex digits", + "type": "string" + }, + "level": { + "description": "unit or integration: only this level; both when absent", + "type": "string" + }, + "repository": { + "description": "/", + "type": "string" + }, + "tests": { + "description": "a go test -run pattern: only those tests run, and nothing is kept", + "type": "string" + }, + "trunk": { + "description": "the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent", + "type": "string" + } + }, + "required": [ + "repository", + "head" + ], + "type": "object" + }, + "answer": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the try's id" + }, + "state": { + "type": "string", + "description": "its state" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "try-read", + "description": "Where a try stands, as stored: queued, running, or its end (pass, fail, or error when it could not run), with what was said of it, the head and the trunk commit it was merged onto, the time it took and what failed first. Read-only.", + "optional": true, + "input": { + "properties": { + "id": { + "description": "the try's id, try-", + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "answer": { + "type": "object", + "description": "the try: its state, its steps and its verdict when it has one" + } + }, + { + "name": "try-end", + "description": "End a try that waits or runs, as error, by hand: say why (kept in what it says). Its place among the tries is free at once and the next try of its head asks anew with a new id. A try that ended already is refused.", + "optional": true, + "input": { + "properties": { + "id": { + "description": "the try's id, try-", + "type": "string" + }, + "why": { + "description": "why it is ended, in words", + "type": "string" + } + }, + "required": [ + "id", + "why" + ], + "type": "object" + }, + "answer": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the try" + }, + "ended": { + "type": "boolean", + "description": "it is ended" + } + }, + "required": [ + "id", + "ended" + ] + } + } + ], + "description": "The merge check's tries: start one, read one, end one. The checker's own events stay under its own subjects (ADR 0620 §2).", + "holder-events": [ + { + "name": "verdict", + "subject": "mesh.mod..event.verdict", + "family": "checker.asks", + "body": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "owner/repo" + }, + "pull": { + "type": "integer", + "description": "the pull request's number, for a pull request's check" + }, + "head": { + "type": "string", + "description": "the commit checked" + }, + "verdict": { + "type": "string", + "description": "pass or fail" + }, + "out-of-step": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the generated files out of step with their seat definitions, each path (design 26)" + } + }, + "required": [ + "repository", + "head", + "verdict" + ] + } + }, + { + "name": "tag-wanted", + "subject": "mesh.mod..event.tag-wanted", + "family": "checker.asks", + "body": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "owner/repo" + }, + "pull": { + "type": "integer", + "description": "the pull request" + }, + "head": { + "type": "string", + "description": "its head" + }, + "module": { + "type": "string", + "description": "the group member" + }, + "package": { + "type": "string", + "description": "the package it declares" + }, + "tag": { + "type": "string", + "description": "the tag wanted" + } + }, + "required": [ + "repository", + "head", + "module", + "package" + ] + } + }, + { + "name": "preview.written", + "subject": "mesh.mod..event.preview.written", + "family": "checks" + } + ] +} diff --git a/seats/operator-channel/fixtures/events.json b/seats/operator-channel/fixtures/events.json new file mode 100644 index 0000000..971ce14 --- /dev/null +++ b/seats/operator-channel/fixtures/events.json @@ -0,0 +1,98 @@ +[ + { + "name": "decided for its asker", + "of": "event:decided", + "subject": "mesh.seat.operator-channel.event.decided.mesh-delivery", + "given": { + "ask": "a1", + "asker": "mesh-delivery", + "outcome": "chosen", + "option": "ok", + "label": "Release", + "level": "approve", + "by": { + "who": "operator", + "kind": "telegram", + "identity": "1", + "verified": "user id" + }, + "channel": "telegram", + "at": "2026-11-04T12:00:00Z", + "ask-digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + } + }, + { + "name": "refused under the holder's own subjects", + "of": "holder-event:refused", + "subject": "mesh.mod.messenger.event.refused", + "given": {} + }, + { + "name": "offered on the router's restricted stream", + "of": "holder-event:offered", + "subject": "mesh.mod.messenger.event.offered", + "given": { + "id": "a1", + "asker": "mesh-delivery", + "headline": "release held", + "channels": [ + "telegram" + ] + } + }, + { + "name": "closed once per end", + "of": "holder-event:closed", + "subject": "mesh.mod.messenger.event.closed", + "given": { + "id": "a1", + "asker": "mesh-delivery", + "how": "answered" + } + }, + { + "name": "an ask by its asker", + "of": "accept:ask", + "subject": "mesh.seat.operator-channel.accept.ask.mesh-delivery", + "given": { + "id": "a1", + "headline": "release held", + "explanation": "A delivery waits on your word.", + "options": [ + { + "id": "ok", + "label": "Release", + "does": "releases it", + "level": "approve", + "binds": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + } + ], + "who": "operator", + "expires": "2026-11-05T12:00:00Z", + "on-expiry": "the delivery stays held" + } + }, + { + "name": "a cancel by its asker", + "of": "accept:cancel", + "subject": "mesh.seat.operator-channel.accept.cancel.mesh-delivery", + "given": { + "id": "a1" + } + }, + { + "name": "a condition by its publisher", + "of": "accept:condition", + "subject": "mesh.seat.operator-channel.accept.condition.mesh-watcher", + "given": { + "v": 1, + "key": "backup.ace.failed", + "subject": { + "scope": "node", + "id": "ace" + }, + "state": "raised", + "severity": "warning" + } + } +] diff --git a/seats/operator-channel/seat.json b/seats/operator-channel/seat.json new file mode 100644 index 0000000..26d0e95 --- /dev/null +++ b/seats/operator-channel/seat.json @@ -0,0 +1,560 @@ +{ + "format": 1, + "seat": "operator-channel", + "scope": "mesh", + "grain": "whole", + "decision": "novox/hq ADR 0259", + "holder-declares": true, + "description": "Asking a person and answering on a channel (ADR 0259): held by the router, the messenger module, whose manifest declares the seat; written from that seat entry (design 26).", + "verbs": [ + { + "name": "open", + "description": "What is open now: every message the operator was sent about something still wrong, urgent first, oldest first.", + "input": { + "type": "object", + "properties": {}, + "description": "no arguments" + }, + "answer": { + "type": "object", + "description": "the open messages" + } + }, + { + "name": "history", + "description": "What was said to the operator lately, newest first, the refusals, and what waits to be sent on each channel.", + "input": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "description": "at most this many of each (default 50)" + } + } + }, + "answer": { + "type": "object", + "description": "the sends, refusals and what waits" + } + }, + { + "name": "notify", + "description": "Tell the operator something, as a module that uses the seat: a key, urgent or warning, one line, and what it is about; clear says it is over.", + "input": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "the message's key" + }, + "severity": { + "type": "string", + "enum": [ + "urgent", + "warning" + ] + }, + "summary": { + "type": "string", + "description": "one line" + }, + "subject": { + "type": "string", + "description": "what it is about" + }, + "headline": { + "type": "string", + "description": "plain words" + }, + "explanation": { + "type": "string", + "description": "one or two plain sentences" + }, + "resolved": { + "type": "string", + "description": "the line said when it is over" + }, + "clear": { + "type": "boolean", + "description": "it is over" + } + } + }, + "answer": { + "type": "object", + "description": "what was sent" + } + }, + { + "name": "tell", + "description": "Tell the operator a plain notice that needs no answer and opens no condition (ADR 0380).", + "input": { + "type": "object", + "properties": { + "headline": { + "type": "string", + "description": "plain words" + }, + "lines": { + "type": "array", + "items": { + "type": "string" + }, + "description": "one or two plain lines" + }, + "subject": { + "type": "string", + "description": "what it is about" + } + } + }, + "answer": { + "type": "object", + "description": "what was sent" + } + }, + { + "name": "asks", + "description": "The asks the router holds: open first, then the last month's, each with how it ended.", + "input": { + "type": "object", + "properties": {}, + "description": "no arguments" + }, + "answer": { + "type": "object", + "description": "the asks" + } + } + ], + "accepts": [ + { + "name": "ask", + "by-caller": true, + "body": { + "$ref": "#/defs/Ask" + } + }, + { + "name": "cancel", + "by-caller": true, + "body": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the ask's id" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "condition", + "by-caller": true, + "body": { + "$ref": "#/defs/Condition" + } + } + ], + "events": [ + { + "name": "decided", + "by-caller": true, + "body": { + "$ref": "#/defs/Warrant" + } + } + ], + "family": { + "name": "operator-channel.decided", + "max-age-days": 30 + }, + "records": [ + { + "name": "asks", + "key": ".", + "value": { + "type": "object", + "description": "one ask the router holds and how it stands" + } + } + ], + "holder-events": [ + { + "name": "refused", + "subject": "mesh.mod..event.refused" + }, + { + "name": "offered", + "subject": "mesh.mod..event.offered", + "family": "the router's restricted stream (design 55 § 11)", + "body": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the ask's id" + }, + "asker": { + "type": "string", + "description": "its asker" + }, + "headline": { + "type": "string", + "description": "its headline" + }, + "channels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the channels it was offered on" + } + }, + "required": [ + "id", + "asker" + ] + } + }, + { + "name": "closed", + "subject": "mesh.mod..event.closed", + "family": "the router's restricted stream (design 55 § 11)", + "body": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the ask's id" + }, + "asker": { + "type": "string", + "description": "its asker" + }, + "how": { + "type": "string", + "enum": [ + "answered", + "expired", + "cancelled", + "replaced", + "unlinked" + ] + } + }, + "required": [ + "id", + "asker", + "how" + ] + } + } + ], + "defs": { + "Ask": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the asker's own id" + }, + "headline": { + "type": "string", + "description": "a few plain words" + }, + "explanation": { + "type": "string", + "description": "what happened and what it means" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/defs/Option" + }, + "description": "the answers it offers" + }, + "who": { + "type": "string", + "description": "who may answer: operator" + }, + "expires": { + "type": "string", + "description": "when it ends unanswered, RFC 3339" + }, + "on-expiry": { + "type": "string", + "description": "what the asker then does" + }, + "about": { + "type": "string", + "description": "what it is about: a newer ask about it replaces the older" + }, + "urgent": { + "type": "boolean", + "description": "urgent" + }, + "whole": { + "type": "string", + "description": "the explanation with its values whole" + }, + "details": { + "type": "string", + "description": "its secondary words" + }, + "whole-details": { + "type": "string", + "description": "the details whole" + } + }, + "required": [ + "id", + "headline", + "explanation", + "options", + "who", + "expires", + "on-expiry" + ] + }, + "Option": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "the option's id" + }, + "label": { + "type": "string", + "description": "the button's label" + }, + "does": { + "type": "string", + "description": "what it does, in plain words" + }, + "level": { + "type": "string", + "description": "the proof its answer needs" + }, + "binds": { + "type": "string", + "description": "the digest of exactly what the asker performs when it is chosen" + } + }, + "required": [ + "id", + "label", + "does", + "level" + ] + }, + "Warrant": { + "type": "object", + "properties": { + "ask": { + "type": "string", + "description": "the ask" + }, + "asker": { + "type": "string", + "description": "its asker" + }, + "about": { + "type": "string", + "description": "what it was about" + }, + "outcome": { + "type": "string", + "enum": [ + "chosen", + "expired", + "cancelled", + "replaced", + "refused" + ] + }, + "option": { + "type": "string", + "description": "the option chosen" + }, + "label": { + "type": "string", + "description": "its label" + }, + "level": { + "type": "string", + "description": "its level" + }, + "by": { + "$ref": "#/defs/Person" + }, + "channel": { + "type": "string", + "description": "the module it came through" + }, + "proofs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the proofs present" + }, + "at": { + "type": "string", + "description": "when, RFC 3339" + }, + "ask-digest": { + "type": "string", + "description": "the digest of the ask as the router took it" + }, + "words": { + "type": "string", + "description": "why it ended without a choice, or what refused it" + } + }, + "required": [ + "ask", + "asker", + "outcome", + "at" + ] + }, + "Person": { + "type": "object", + "properties": { + "who": { + "type": "string", + "description": "the role" + }, + "kind": { + "type": "string", + "description": "the channel kind" + }, + "identity": { + "type": "string", + "description": "the account" + }, + "display": { + "type": "string", + "description": "the name shown" + }, + "verified": { + "type": "string", + "description": "how the router knew" + } + }, + "required": [ + "who", + "kind", + "identity", + "verified" + ] + }, + "Condition": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the form's version, 1" + }, + "key": { + "type": "string", + "description": "the condition's key" + }, + "severity": { + "type": "string", + "description": "urgent or warning" + }, + "subject": { + "type": "object", + "properties": { + "scope": { + "type": "string", + "description": "scope" + }, + "id": { + "type": "string", + "description": "id" + }, + "machine": { + "type": "string", + "description": "machine" + } + }, + "required": [ + "scope", + "id" + ] + }, + "state": { + "type": "string", + "description": "raised or cleared" + }, + "summary": { + "type": "string", + "description": "one line" + }, + "headline": { + "type": "string", + "description": "plain words" + }, + "needs": { + "type": "string", + "description": "what it needs" + }, + "explanation": { + "type": "string", + "description": "plain words" + }, + "resolved": { + "type": "string", + "description": "the line said when it is over" + }, + "actions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "label" + }, + "verb": { + "type": "string", + "description": "verb" + }, + "machine": { + "type": "string", + "description": "machine" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "level": { + "type": "string", + "description": "level" + } + }, + "required": [ + "label", + "verb" + ] + }, + "description": "the answers it offers" + } + }, + "required": [ + "v", + "key", + "subject", + "state" + ] + } + } +}