From c5cf0126f99907f014a68c0cc4fa4f8075c6e7a1 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 22:46:44 +0100 Subject: [PATCH] K27 (owner change): the seat mesh-conditions' condition-changed gains the change value owner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §540, §685, A-58 (findings-stated's kinds, §580, and attempt-changed, §541/§665, were built by K13 and K12; seatgen -check clean) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- .../go/clients/meshconditionsclient/client.go | 1 + .../meshconditionsclient/client_test.go | 3 ++- .../go/seats/meshconditions/contract.go | 1 + .../go/seats/meshconditions/contract_test.go | 2 ++ .../mesh-conditions/fixtures/events.json | 24 +++++++++++++++++++ .../seats/mesh-conditions/seat.json | 3 ++- .../ts/seats/meshconditions/index.d.ts | 2 +- 7 files changed, 33 insertions(+), 3 deletions(-) diff --git a/health-and-repair/go/clients/meshconditionsclient/client.go b/health-and-repair/go/clients/meshconditionsclient/client.go index c2fbc08..cf309d5 100644 --- a/health-and-repair/go/clients/meshconditionsclient/client.go +++ b/health-and-repair/go/clients/meshconditionsclient/client.go @@ -72,6 +72,7 @@ const ( ChangeUnsilenced = "silence-ended" ChangeCleared = "cleared" ChangeWords = "words" + ChangeOwner = "owner" ) var unsafeKey = regexp.MustCompile(`[^A-Za-z0-9_=/-]`) diff --git a/health-and-repair/go/clients/meshconditionsclient/client_test.go b/health-and-repair/go/clients/meshconditionsclient/client_test.go index f377f6d..768d24b 100644 --- a/health-and-repair/go/clients/meshconditionsclient/client_test.go +++ b/health-and-repair/go/clients/meshconditionsclient/client_test.go @@ -32,7 +32,8 @@ func TestAFindingsKeyAndTheOrderOfConditions(t *testing.T) { t.Error("silence is until its end") } if EventFor(ChangeReopened) != seat.EventConditionRaised || EventFor(ChangeSilenced) != seat.EventConditionChanged || - EventFor(ChangeCleared) != seat.EventConditionCleared { + EventFor(ChangeCleared) != seat.EventConditionCleared || EventFor(ChangeOwner) != seat.EventConditionChanged || + ChangeOwner != seat.ConditionEventChangeOwner { t.Error("a change is said as its event") } if a := SilenceAction("k"); a.Verb != "mesh-conditions.silence" || ShowKeeper("k") != "mesh-conditions.conditions key=k" { diff --git a/health-and-repair/go/seats/meshconditions/contract.go b/health-and-repair/go/seats/meshconditions/contract.go index f07e37f..4772056 100644 --- a/health-and-repair/go/seats/meshconditions/contract.go +++ b/health-and-repair/go/seats/meshconditions/contract.go @@ -264,6 +264,7 @@ const ( ConditionEventChangeSilenceEnded = "silence-ended" ConditionEventChangeCleared = "cleared" ConditionEventChangeWords = "words" + ConditionEventChangeOwner = "owner" ) // The values of ConditionEvent's severity. diff --git a/health-and-repair/go/seats/meshconditions/contract_test.go b/health-and-repair/go/seats/meshconditions/contract_test.go index ea0fedb..59c5275 100644 --- a/health-and-repair/go/seats/meshconditions/contract_test.go +++ b/health-and-repair/go/seats/meshconditions/contract_test.go @@ -23,6 +23,8 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v FindingsAskedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-conditions\\.event\\.findings-asked$")}, {name: "a condition raised with its owner and what it waits on (design 47 G 685)", subject: "mesh.seat.mesh-conditions.event.condition-raised", given: "{\"at\":\"2026-11-11T10:00:00Z\",\"change\":\"raised\",\"event\":\"condition-raised\",\"key\":\"module.mesh-host.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"owner\":\"forge:jochen\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"node-engine\"}", wire: "{\"at\":\"2026-11-11T10:00:00Z\",\"change\":\"raised\",\"event\":\"condition-raised\",\"key\":\"module.mesh-host.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"owner\":\"forge:jochen\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"node-engine\"}", decode: func(b []byte) (any, error) { var v ConditionRaisedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-conditions\\.event\\.condition-raised$")}, + {name: "a condition's owner handed to the operator after two hours (design 47 G 540, A-58)", subject: "mesh.seat.mesh-conditions.event.condition-changed", given: "{\"at\":\"2026-11-11T12:00:00Z\",\"change\":\"owner\",\"event\":\"condition-changed\",\"key\":\"module.mesh-delivery.ace.pipeline-failed\",\"kind\":\"pipeline-failed\",\"owner\":\"operator-channel\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"runner\",\"subject\":{\"id\":\"mesh-delivery.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a head failed its pipeline\",\"waiting-on\":\"operator-channel\",\"was\":\"forge:jochen\"}", wire: "{\"at\":\"2026-11-11T12:00:00Z\",\"change\":\"owner\",\"event\":\"condition-changed\",\"key\":\"module.mesh-delivery.ace.pipeline-failed\",\"kind\":\"pipeline-failed\",\"owner\":\"operator-channel\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"runner\",\"subject\":{\"id\":\"mesh-delivery.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a head failed its pipeline\",\"waiting-on\":\"operator-channel\",\"was\":\"forge:jochen\"}", + decode: func(b []byte) (any, error) { var v ConditionChangedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-conditions\\.event\\.condition-changed$")}, {name: "a condition raised from a finding, as the keeper holds it (captured; empty fields left out)", subject: "", given: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator; the details name it and the line to type. A directory of mesh-build-node was already on ace, with another owner or mode than mesh-build-node declares.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"key\":\"module.mesh-build-node.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"last-observed\":\"2026-10-26T20:19:41.474823121Z\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.076843727Z\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}", wire: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator; the details name it and the line to type. A directory of mesh-build-node was already on ace, with another owner or mode than mesh-build-node declares.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"key\":\"module.mesh-build-node.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"last-observed\":\"2026-10-26T20:19:41.474823121Z\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.076843727Z\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}", decode: func(b []byte) (any, error) { var v ConditionsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, {name: "a condition with an empty evidence trail of headline (captured; empty fields left out)", subject: "", given: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-30T13:03:46.432309782Z\",\"said\":\"The backup of postgres failed on ace\"}],\"explanation\":\"Needs you: answer whether to back up again or go on without a backup. The backup before applying postgres at 978b5db8cb4e on ace failed: node-backup.now could not be asked: not asked: nothing on this machine answers mesh.seat.node-backup.tool.now.ace.\",\"headline\":\"The backup of postgres failed on ace\",\"key\":\"module.ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f.backup\",\"kind\":\"backup\",\"last-observed\":\"2026-10-30T13:03:46.432309782Z\",\"needs\":\"answer whether to back up again or go on without a backup\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.069818178Z\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"assigner.backup\",\"subject\":{\"id\":\"ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"The backup of postgres failed on ace\"}", wire: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-30T13:03:46.432309782Z\",\"said\":\"The backup of postgres failed on ace\"}],\"explanation\":\"Needs you: answer whether to back up again or go on without a backup. The backup before applying postgres at 978b5db8cb4e on ace failed: node-backup.now could not be asked: not asked: nothing on this machine answers mesh.seat.node-backup.tool.now.ace.\",\"headline\":\"The backup of postgres failed on ace\",\"key\":\"module.ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f.backup\",\"kind\":\"backup\",\"last-observed\":\"2026-10-30T13:03:46.432309782Z\",\"needs\":\"answer whether to back up again or go on without a backup\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.069818178Z\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"assigner.backup\",\"subject\":{\"id\":\"ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"The backup of postgres failed on ace\"}", diff --git a/health-and-repair/seats/mesh-conditions/fixtures/events.json b/health-and-repair/seats/mesh-conditions/fixtures/events.json index eebbd31..e6fbff3 100644 --- a/health-and-repair/seats/mesh-conditions/fixtures/events.json +++ b/health-and-repair/seats/mesh-conditions/fixtures/events.json @@ -42,5 +42,29 @@ "at": "2026-11-11T10:00:00Z", "change": "raised" } + }, + { + "name": "a condition's owner handed to the operator after two hours (design 47 G 540, A-58)", + "of": "event:condition-changed", + "subject": "mesh.seat.mesh-conditions.event.condition-changed", + "given": { + "key": "module.mesh-delivery.ace.pipeline-failed", + "kind": "pipeline-failed", + "subject": { + "scope": "module", + "id": "mesh-delivery.ace", + "machine": "ace" + }, + "severity": "warning", + "summary": "a head failed its pipeline", + "source": "runner", + "resolver": "self", + "owner": "operator-channel", + "waiting-on": "operator-channel", + "event": "condition-changed", + "at": "2026-11-11T12:00:00Z", + "change": "owner", + "was": "forge:jochen" + } } ] diff --git a/health-and-repair/seats/mesh-conditions/seat.json b/health-and-repair/seats/mesh-conditions/seat.json index e5d45a8..256519b 100644 --- a/health-and-repair/seats/mesh-conditions/seat.json +++ b/health-and-repair/seats/mesh-conditions/seat.json @@ -651,7 +651,8 @@ "silenced", "silence-ended", "cleared", - "words" + "words", + "owner" ] }, "was": { diff --git a/health-and-repair/ts/seats/meshconditions/index.d.ts b/health-and-repair/ts/seats/meshconditions/index.d.ts index 22c4ea8..3deca6b 100644 --- a/health-and-repair/ts/seats/meshconditions/index.d.ts +++ b/health-and-repair/ts/seats/meshconditions/index.d.ts @@ -86,7 +86,7 @@ export interface ConditionEventTriedItem { export interface ConditionEvent { "actions"?: Action[]; "at": string; - "change": "raised" | "reopened" | "severity" | "resolver" | "silenced" | "silence-ended" | "cleared" | "words"; + "change": "raised" | "reopened" | "severity" | "resolver" | "silenced" | "silence-ended" | "cleared" | "words" | "owner"; "cleared"?: string; "count"?: number; "epoch"?: number;