activity-core corrected us: it governs Temporal schedule overlap, not per-message suppression, and there is no dedupe at the sink — so an unread message re-emits hourly until read. Removed the field rather than leave it implying a guarantee it does not give, and kept the repetition deliberately: an hourly line in the progress log is a cheap price for the failure this exists to prevent, and it stops when somebody reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.6 KiB
Markdown
99 lines
3.6 KiB
Markdown
---
|
|
id: hourly-register-inbox-watch
|
|
name: Hourly Register Inbox Watch
|
|
type: activity-definition
|
|
version: "1.0"
|
|
enabled: true
|
|
owner: risk-nexus
|
|
governance: custodian
|
|
status: active
|
|
created: "2026-08-20"
|
|
updated: "2026-08-20"
|
|
trigger:
|
|
type: cron
|
|
cron_expression: "5 * * * *"
|
|
timezone: Europe/Berlin
|
|
misfire_policy: skip
|
|
context_sources:
|
|
- type: state-hub
|
|
query: hub_inbox
|
|
required: true
|
|
params:
|
|
to_agent: risk-nexus
|
|
unread_only: true
|
|
bind_to: context.inbox
|
|
report_sinks:
|
|
- type: state-hub-progress
|
|
event_type: risk_register_inbox
|
|
author: activity-core
|
|
---
|
|
|
|
# Hourly Register Inbox Watch
|
|
|
|
Runs hourly at :05 Berlin time. Fires only when something is actually waiting.
|
|
|
|
## Why hourly, and why the inbox specifically
|
|
|
|
`docs/method/review.md` sets an adaptive cadence whose shortest rung is one
|
|
hour, so hourly is the finest granularity the register can act on. Anything
|
|
faster would report work that cannot yet be due.
|
|
|
|
The inbox is the trigger rather than the clock because of what happened on
|
|
2026-08-19: this register graded `RISK-F-0001` `critical` and prepared an
|
|
escalation while two messages sat unread — one narrowing the exposure, one
|
|
reporting the fix. Both changed the grade. Reading the inbox is now question
|
|
zero of every check (`docs/method/check-procedure.md`), and this activity is
|
|
the mechanism that stops it depending on somebody remembering.
|
|
|
|
An unread message addressed to `risk-nexus` is, by construction, a claim that
|
|
something in the register may have moved.
|
|
|
|
## It will repeat, and that is the deliberate choice
|
|
|
|
`activity-core` corrected us on 2026-08-20: `dedupe_key_strategy` governs
|
|
Temporal schedule overlap, not per-message suppression, and cron workflow ids
|
|
include `scheduled_for` — so an unread message re-emits an instruction **every
|
|
hour until it is read**. There is no dedupe at the sink either.
|
|
|
|
We removed the field rather than leave it there implying a guarantee it does
|
|
not give.
|
|
|
|
The repetition is kept, on purpose. An unread message addressed to this
|
|
register is a claim that something in it may have moved, and the failure this
|
|
activity exists to prevent — grading `RISK-F-0001` `critical` while its fix
|
|
notice sat unread — cost a wrong grade and a nearly-sent escalation. An hourly
|
|
line in the progress log is a cheap price for that, and it stops the moment
|
|
somebody reads the message.
|
|
|
|
**Reading a message includes marking it read.** That is what clears the nag,
|
|
and it is part of question zero rather than a courtesy.
|
|
|
|
If this ever becomes real noise — an unattended weekend, a message nobody can
|
|
action — the fix `activity-core` named is a stable completion key gating the
|
|
rule, not a longer interval. A longer interval would trade a visible cost for
|
|
an invisible one.
|
|
|
|
## What it does not do
|
|
|
|
It does not grade, close, publish or escalate anything. Severity, disclosure
|
|
and escalation are judgements this repo makes with a human or an agent session
|
|
doing the reading — never a cron. This activity's entire job is to make sure
|
|
somebody looks.
|
|
|
|
```rule
|
|
id: inbox-has-spoken
|
|
for_each: context.inbox
|
|
bind_as: message
|
|
condition: 'context.message.read_at == None'
|
|
action:
|
|
task_template: 'risk-nexus: read {context.message.from_agent} before checking'
|
|
description: >-
|
|
Unread message to risk-nexus from {context.message.from_agent}:
|
|
"{context.message.subject}". Read it before any check or grading — this is
|
|
question zero in docs/method/check-procedure.md. Then run `make due` in
|
|
risk-nexus and work the list, recording each outcome with
|
|
`make checked ARGS="<finding-id> clean|moved"`.
|
|
target_repo: risk-nexus
|
|
priority: high
|
|
labels: ["risk-register", "inbox", "question-zero"]
|
|
```
|