Make delivery and integration failures explicit

Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
2026-08-13 02:50:59 +04:00
parent da9114b623
commit 35c6ff5a71
67 changed files with 3174 additions and 477 deletions
+21 -15
View File
@@ -63,14 +63,15 @@ type Nudge struct {
// DeliveryAttempt — one row of the delivery outbox. Times are formatted by the
// reader; Completed is nil while the attempt is still pending.
type DeliveryAttempt struct {
ID int64 `json:"id"`
Kind string `json:"kind"`
Rule string `json:"rule,omitempty"`
ReminderID int64 `json:"reminder_id,omitempty"`
Channel string `json:"channel"`
Status string `json:"status"`
Created time.Time `json:"created"`
Completed *time.Time `json:"completed,omitempty"`
ID int64 `json:"id"`
Kind string `json:"kind"`
Rule string `json:"rule,omitempty"`
ReminderID int64 `json:"reminder_id,omitempty"`
DeliveryGroup string `json:"delivery_group,omitempty"`
Channel string `json:"channel"`
Status string `json:"status"`
Created time.Time `json:"created"`
Completed *time.Time `json:"completed,omitempty"`
}
// Note — a recall/preference item; ranked by embedding cosine on query.
@@ -85,13 +86,18 @@ type Note struct {
// Reminder — user-stated future intent; fires once or recurring (if cron set).
type Reminder struct {
ID int64 `json:"id"`
CreatedTs time.Time `json:"created_ts"`
FireTs time.Time `json:"fire_ts"`
NextFireTs time.Time `json:"next_fire_ts"`
Payload string `json:"payload"`
Status string `json:"status"` // pending|fired|cancelled
Cron string `json:"cron"`
ID int64 `json:"id"`
CreatedTs time.Time `json:"created_ts"`
FireTs time.Time `json:"fire_ts"`
NextFireTs time.Time `json:"next_fire_ts"`
Payload string `json:"payload"`
Status string `json:"status"` // pending|fired|cancelled
Cron string `json:"cron"`
DeliveryGroup string `json:"delivery_group,omitempty"`
DeliveryAttempts int `json:"delivery_attempts,omitempty"`
NextAttemptTs time.Time `json:"next_attempt_ts,omitempty"`
DeliveryBlockedTs time.Time `json:"delivery_blocked_ts,omitempty"`
DeliveryBlockedError string `json:"delivery_blocked_error,omitempty"`
}
// Presence — the read the phraser / delivery modules need to decide channel
+3 -2
View File
@@ -42,8 +42,9 @@ var mapErrPairs = []struct {
// and fails TestMapErrCoversEveryStoreSentinel, which is the point: whether a
// module can branch on an error is a decision, not a default.
var unmappedStoreErrors = map[string]string{
"ErrKeyLen": "unlock path — the key never crosses CoreAPI",
"ErrDecrypt": "unlock path — the key never crosses CoreAPI",
"ErrKeyLen": "unlock path — the key never crosses CoreAPI",
"ErrDecrypt": "unlock path — the key never crosses CoreAPI",
"ErrReminderPhrase": "daemon-internal delivery-state validation; modules neither cache reminder presentations nor branch on this verdict",
"ErrToolCmd": "write-side validation of an allowlist mutation; the caller is the owner at a step-up, not a module branching on the verdict",
+13 -8
View File
@@ -149,7 +149,7 @@ func (a *storeAPI) DeliveryAttempts(ctx context.Context, status string, n int) (
return mapRows(as, err, func(at store.DeliveryAttempt) DeliveryAttempt {
out := DeliveryAttempt{
ID: at.ID, Kind: at.Kind, Rule: at.Rule, ReminderID: at.ReminderID,
Channel: at.Channel, Status: at.Status, Created: at.Created,
DeliveryGroup: at.DeliveryGroup, Channel: at.Channel, Status: at.Status, Created: at.Created,
}
if at.HasComplete {
t := at.Completed
@@ -352,13 +352,18 @@ func toTool(t store.Tool) Tool {
func toReminder(r store.Reminder) Reminder {
return Reminder{
ID: r.ID,
CreatedTs: r.CreatedTs,
FireTs: r.FireTs,
NextFireTs: r.NextFireTs,
Payload: r.Payload,
Status: r.Status,
Cron: r.Cron,
ID: r.ID,
CreatedTs: r.CreatedTs,
FireTs: r.FireTs,
NextFireTs: r.NextFireTs,
Payload: r.Payload,
Status: r.Status,
Cron: r.Cron,
DeliveryGroup: r.DeliveryGroup,
DeliveryAttempts: r.DeliveryAttempts,
NextAttemptTs: r.NextAttemptTs,
DeliveryBlockedTs: r.DeliveryBlockedTs,
DeliveryBlockedError: r.DeliveryBlockedError,
}
}