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:
+21
-15
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user