Say each notification in plain words the operator reads at a glance
mesh/merge-gate pass: builds messenger → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/plain-notifications ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request

The desktop popups were walls of plan ids, commits, keys and verb syntax in UTC.
The messenger now shows the condition's headline, explanation and resolved line,
since when in the operator's time zone, and a clearance in one line (hq ADR 0253).
This commit is contained in:
jochen
2026-10-08 13:26:26 +02:00
parent c78b5fc9f5
commit a6296dfd6f
12 changed files with 573 additions and 116 deletions
+20 -4
View File
@@ -36,6 +36,7 @@ type Status struct {
Stale int `json:"events_taken_as_state"` // older than the freshness bound: recorded, never said
Grace bool `json:"in_start_grace,omitempty"`
StateProblem string `json:"state_problem,omitempty"`
TimeZone string `json:"time_zone"` // the zone every time in a message is said in
Rules []string `json:"rules"`
}
@@ -95,7 +96,10 @@ func (h *Holder) Status(listening string) Status {
h.mu.Unlock()
sort.Strings(st.Unsent)
st.Listening = listening
st.TimeZone = h.zone().String()
st.Rules = []string{
"a message reads in one glance: the condition's headline as its title, its explanation, and since when in the operator's time zone; no key, id, commit or command — those stay in the controller's conditions and in this holder's history (novox/hq ADR 0253)",
"a clearance is one line: the condition's resolved line and how long it was open",
"history is never news: what is open is read from the controller on start, every 10 min and when an old event arrives; an event older than that reading is not acted on, one older than 10 min by its own time is state, never a message",
"a clearing of something never said says nothing; an urgent condition open and never said is said, all of them in one message",
"nothing is sent in the first minute after start but that one message",
@@ -213,7 +217,7 @@ func (h *Holder) Test(channel, text string) map[string]string {
out["refused"] = "it carried " + refusal.String() + "; nothing was sent"
return out
}
m := Message{Title: "TEST: " + text, Body: "nothing is wrong; this was asked for"}
m := Message{Title: "Test message: " + text, Body: "Nothing is wrong; this was asked for."}
for _, ch := range h.channels() {
if channel != "" && channel != "both" && channel != ch.Name() {
continue
@@ -240,7 +244,7 @@ func (h *Holder) Test(channel, text string) map[string]string {
// Notify takes a message from a module that uses the seat (research 028 Q5, modules as sources):
// the same shape, rule, cap and deduplication as a condition. Its key is put under `notify.` so it
// can never be taken for one of the controller's conditions.
func (h *Holder) Notify(key, severity, summary, subject string, clear bool) (map[string]any, error) {
func (h *Holder) Notify(key, severity, summary, subject string, clear bool, plain ...string) (map[string]any, error) {
if key == "" {
return nil, errorf("key is required: the same key is the same message")
}
@@ -266,8 +270,20 @@ func (h *Holder) Notify(key, severity, summary, subject string, clear bool) (map
if summary == "" {
return nil, errorf("summary is required: one line in the mesh's words")
}
h.raised(Record{Key: key, Kind: "notice", Subject: subject, Severity: severity, Summary: summary,
Origin: "notify", More: "operator-channel.open"}, false)
rec := Record{Key: key, Kind: "notice", Subject: subject, Severity: severity, Summary: summary,
Origin: "notify", More: "operator-channel.open"}
// The plain words a module may give (novox/hq ADR 0253): a headline, an explanation, a resolved line.
for i, w := range plain {
switch i {
case 0:
rec.Headline = w
case 1:
rec.Explanation = w
case 2:
rec.Resolved = w
}
}
h.raised(rec, false)
h.mu.Lock()
r := h.open[key]
var sent []string