From 43dc4871130760e380c0e476742c6049b8cb3182 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 6 Aug 2026 01:30:41 +0400 Subject: [PATCH 1/3] ipc: one helper for the list reads on the wire (V-575) Fourteen table entries carried the same four lines: call the CoreAPI method, return early on error, swap a nil slice for an empty one so the wire says [] and not null. withParamsSlice holds that once and each entry is now the call it makes. Three id-only request types were the same struct under three names, so the routine transitions use the idReq that was already declared and unused. The revert reply was a map literal on one side and an anonymous struct on the other; revertResp names it. Both are wire-identical. --- internal/ipc/api.go | 15 ++-- internal/ipc/client.go | 12 ++-- internal/ipc/server.go | 159 ++++++++++++----------------------------- 3 files changed, 55 insertions(+), 131 deletions(-) diff --git a/internal/ipc/api.go b/internal/ipc/api.go index 8128700..113c825 100644 --- a/internal/ipc/api.go +++ b/internal/ipc/api.go @@ -594,7 +594,7 @@ type setTaskFieldsReq struct { BlockedOn string `json:"blocked_on,omitempty"` } -// idReq — methods keyed by a single id. +// idReq — methods keyed by a single id, which is every routine transition. type idReq struct { ID int64 `json:"id"` } @@ -652,6 +652,11 @@ type calendarEventsReq struct { type revertReq struct { Key string `json:"key"` } + +// revertResp — the id of the voiding fact the revert wrote. +type revertResp struct { + NewID int64 `json:"new_id"` +} type writeNoteReq struct { Ts time.Time `json:"ts"` Text string `json:"text"` @@ -780,14 +785,6 @@ type listProposedRoutinesResp struct { Routines []ProposedRoutine `json:"routines"` } -type dismissProposedRoutineReq struct { - ID int64 `json:"id"` -} - -type acceptProposedRoutineReq struct { - ID int64 `json:"id"` -} - // IntakeEvent — one entry of the unified intake journal on the wire. Mirrors // event.Event field for field; the ipc package does not import internal/event // so the wire shape stays independent of the in-process type. diff --git a/internal/ipc/client.go b/internal/ipc/client.go index ccba692..946ed37 100644 --- a/internal/ipc/client.go +++ b/internal/ipc/client.go @@ -649,11 +649,11 @@ func (c *Client) ModelStatus(ctx context.Context) (ModelStatusResp, error) { } func (c *Client) DismissProposedRoutine(ctx context.Context, id int64) error { - return c.call(ctx, MethodDismissProposedRoutine, dismissProposedRoutineReq{ID: id}, nil) + return c.call(ctx, MethodDismissProposedRoutine, idReq{ID: id}, nil) } func (c *Client) AcceptProposedRoutine(ctx context.Context, id int64) error { - return c.call(ctx, MethodAcceptProposedRoutine, acceptProposedRoutineReq{ID: id}, nil) + return c.call(ctx, MethodAcceptProposedRoutine, idReq{ID: id}, nil) } func (c *Client) Chat(ctx context.Context, conversation, text string) (ChatReply, error) { @@ -713,13 +713,11 @@ func (c *Client) DayPlan(ctx context.Context) (DayPlan, error) { } func (c *Client) RevertFact(ctx context.Context, key string) (int64, error) { - var result struct { - NewID int64 `json:"new_id"` - } - if err := c.call(ctx, MethodRevertFact, map[string]string{"key": key}, &result); err != nil { + var r revertResp + if err := c.call(ctx, MethodRevertFact, revertReq{Key: key}, &r); err != nil { return 0, err } - return result.NewID, nil + return r.NewID, nil } // Ping asks whether the daemon is there, and whether it is locked. It is not a diff --git a/internal/ipc/server.go b/internal/ipc/server.go index b421aed..6e7d590 100644 --- a/internal/ipc/server.go +++ b/internal/ipc/server.go @@ -335,6 +335,22 @@ func withoutParams[R any](fn func(ctx context.Context, api CoreAPI) (R, error)) } } +// withParamsSlice is withParams for a list read. It replaces a nil slice with +// an empty one so the wire carries [] rather than null, which every reader of +// these methods relies on. +func withParamsSlice[P any, E any](fn func(ctx context.Context, api CoreAPI, p P) ([]E, error)) handlerFunc { + return withParams(func(ctx context.Context, api CoreAPI, p P) ([]E, error) { + out, err := fn(ctx, api, p) + if err != nil { + return nil, err + } + if out == nil { + out = []E{} + } + return out, nil + }) +} + // methodTable — one entry per CoreAPI-backed method. Built once at package // init, not per-Server and not per-dispatch: entries close over nothing but // the CoreAPI method being called, and dispatch passes in the *current* @@ -373,15 +389,8 @@ var methodTable = map[Method]handlerFunc{ MethodMarkReminder: withParamsVoid(func(ctx context.Context, api CoreAPI, p markReminderReq) error { return api.MarkReminder(ctx, p.ID, p.Status) }), - MethodListReminders: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]Reminder, error) { - out, err := api.ListReminders(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Reminder{} - } - return out, nil + MethodListReminders: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]Reminder, error) { + return api.ListReminders(ctx, p.N) }), MethodRecordNudge: withParams(func(ctx context.Context, api CoreAPI, p recordNudgeReq) (idResp, error) { id, err := api.RecordNudge(ctx, p.Rule, p.Channel, p.Message, p.Ts) @@ -390,109 +399,39 @@ var methodTable = map[Method]handlerFunc{ MethodResolveNudge: withParamsVoid(func(ctx context.Context, api CoreAPI, p resolveNudgeReq) error { return api.ResolveNudge(ctx, p.ID, p.Outcome, p.Ts) }), - MethodRecentOutcomes: withParams(func(ctx context.Context, api CoreAPI, p outcomesReq) ([]string, error) { - out, err := api.RecentOutcomes(ctx, p.Rule, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []string{} // stable non-null on the wire - } - return out, nil + MethodRecentOutcomes: withParamsSlice(func(ctx context.Context, api CoreAPI, p outcomesReq) ([]string, error) { + return api.RecentOutcomes(ctx, p.Rule, p.N) }), - MethodRecentFacts: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]Fact, error) { - out, err := api.RecentFacts(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Fact{} - } - return out, nil + MethodRecentFacts: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]Fact, error) { + return api.RecentFacts(ctx, p.N) }), - MethodRecentActiveFacts: withParams(func(ctx context.Context, api CoreAPI, p kindNReq) ([]Fact, error) { - out, err := api.RecentActiveFactsByKind(ctx, p.Kind, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Fact{} - } - return out, nil + MethodRecentActiveFacts: withParamsSlice(func(ctx context.Context, api CoreAPI, p kindNReq) ([]Fact, error) { + return api.RecentActiveFactsByKind(ctx, p.Kind, p.N) }), - MethodCalendarEvents: withParams(func(ctx context.Context, api CoreAPI, p calendarEventsReq) ([]Fact, error) { - out, err := api.CalendarEvents(ctx, p.From, p.To) - if err != nil { - return nil, err - } - if out == nil { - out = []Fact{} - } - return out, nil + MethodCalendarEvents: withParamsSlice(func(ctx context.Context, api CoreAPI, p calendarEventsReq) ([]Fact, error) { + return api.CalendarEvents(ctx, p.From, p.To) }), - MethodRecentEcoTraces: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]EcosystemTrace, error) { - out, err := api.RecentEcosystemTraces(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []EcosystemTrace{} - } - return out, nil + MethodRecentEcoTraces: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]EcosystemTrace, error) { + return api.RecentEcosystemTraces(ctx, p.N) }), - MethodDeliveryAttempts: withParams(func(ctx context.Context, api CoreAPI, p deliveryAttemptsReq) ([]DeliveryAttempt, error) { - out, err := api.DeliveryAttempts(ctx, p.Status, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []DeliveryAttempt{} - } - return out, nil + MethodDeliveryAttempts: withParamsSlice(func(ctx context.Context, api CoreAPI, p deliveryAttemptsReq) ([]DeliveryAttempt, error) { + return api.DeliveryAttempts(ctx, p.Status, p.N) }), - MethodRecentNudges: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]Nudge, error) { - out, err := api.RecentNudges(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Nudge{} - } - return out, nil + MethodRecentNudges: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]Nudge, error) { + return api.RecentNudges(ctx, p.N) }), MethodWriteNote: withParams(func(ctx context.Context, api CoreAPI, p writeNoteReq) (idResp, error) { id, err := api.WriteNote(ctx, p.Ts, p.Text, p.Embedding, p.Source) return idResp{ID: id}, err }), - MethodQueryNotes: withParams(func(ctx context.Context, api CoreAPI, p queryNotesReq) ([]Note, error) { - out, err := api.QueryNotes(ctx, p.Embedding, p.K) - if err != nil { - return nil, err - } - if out == nil { - out = []Note{} - } - return out, nil + MethodQueryNotes: withParamsSlice(func(ctx context.Context, api CoreAPI, p queryNotesReq) ([]Note, error) { + return api.QueryNotes(ctx, p.Embedding, p.K) }), - MethodRecentNotesFromSource: withParams(func(ctx context.Context, api CoreAPI, p sourceNReq) ([]Note, error) { - out, err := api.RecentNotesFromSource(ctx, p.Prefix, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Note{} - } - return out, nil + MethodRecentNotesFromSource: withParamsSlice(func(ctx context.Context, api CoreAPI, p sourceNReq) ([]Note, error) { + return api.RecentNotesFromSource(ctx, p.Prefix, p.N) }), - MethodRecentNotes: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]Note, error) { - out, err := api.RecentNotes(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []Note{} - } - return out, nil + MethodRecentNotes: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]Note, error) { + return api.RecentNotes(ctx, p.N) }), MethodProposeTool: withParams(func(ctx context.Context, api CoreAPI, p proposeToolReq) (proposeToolResp, error) { ok, err := api.ProposeTool(ctx, p.Name, p.Utterance, p.Scope, p.Ts) @@ -563,18 +502,15 @@ var methodTable = map[Method]handlerFunc{ } return listProposedRoutinesResp{Routines: out}, nil }), - MethodDismissProposedRoutine: withParamsVoid(func(ctx context.Context, api CoreAPI, p dismissProposedRoutineReq) error { + MethodDismissProposedRoutine: withParamsVoid(func(ctx context.Context, api CoreAPI, p idReq) error { return api.DismissProposedRoutine(ctx, p.ID) }), - MethodAcceptProposedRoutine: withParamsVoid(func(ctx context.Context, api CoreAPI, p acceptProposedRoutineReq) error { + MethodAcceptProposedRoutine: withParamsVoid(func(ctx context.Context, api CoreAPI, p idReq) error { return api.AcceptProposedRoutine(ctx, p.ID) }), - MethodRevertFact: withParams(func(ctx context.Context, api CoreAPI, p revertReq) (map[string]int64, error) { + MethodRevertFact: withParams(func(ctx context.Context, api CoreAPI, p revertReq) (revertResp, error) { newID, err := api.RevertFact(ctx, p.Key) - if err != nil { - return nil, err - } - return map[string]int64{"new_id": newID}, nil + return revertResp{NewID: newID}, err }), MethodChat: withParams(func(ctx context.Context, api CoreAPI, p chatReq) (chatResp, error) { reply, err := api.Chat(ctx, p.Conversation, p.Text) @@ -599,15 +535,8 @@ var methodTable = map[Method]handlerFunc{ MethodMorningStatus: withoutParams(func(ctx context.Context, api CoreAPI) ([]MorningRoutineStatus, error) { return api.MorningStatus(ctx) }), - MethodRecentEvents: withParams(func(ctx context.Context, api CoreAPI, p nReq) ([]IntakeEvent, error) { - out, err := api.RecentEvents(ctx, p.N) - if err != nil { - return nil, err - } - if out == nil { - out = []IntakeEvent{} - } - return out, nil + MethodRecentEvents: withParamsSlice(func(ctx context.Context, api CoreAPI, p nReq) ([]IntakeEvent, error) { + return api.RecentEvents(ctx, p.N) }), MethodMCPServers: withoutParams(func(ctx context.Context, api CoreAPI) ([]MCPServerStatus, error) { out, err := api.MCPServers(ctx) From 7262310fce5230b1d34737e384c0d16c95cf453b Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 6 Aug 2026 01:31:03 +0400 Subject: [PATCH 2/3] ipc: the capability methods share one dispatch shape (V-575) Thirteen arms of dispatch spelled out the same handler: nil check, unmarshal, call, marshal, and a hand-written unknown-method error at the bottom of each. callDirect, callDirectNoParams and callDirectVoid hold the three shapes those arms come in, so the switch now says which Server field backs which method and nothing else. The nil check is the load-bearing part and it is unchanged: a nil field is the capability being unconfigured on this box, and the wire still answers ErrUnknownMethod. WrapKeyFn and UnlockFn keep their own arms because they take apart the request rather than passing it through. No wire change. --- internal/ipc/server.go | 231 +++++++++++++++-------------------------- 1 file changed, 82 insertions(+), 149 deletions(-) diff --git a/internal/ipc/server.go b/internal/ipc/server.go index 6e7d590..4d035a3 100644 --- a/internal/ipc/server.go +++ b/internal/ipc/server.go @@ -583,180 +583,113 @@ func (s *Server) dispatch(ctx context.Context, req Request) (json.RawMessage, er return marshalResult(PingResp{Alive: true, Locked: locked}), nil case MethodAssertStepUp: - if s.StepUp != nil { - return marshalResult(nil), s.StepUp(ctx) + if s.StepUp == nil { + return nil, unknownMethod(req.Method) } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) + return marshalResult(nil), s.StepUp(ctx) case MethodStoreEncryptionKey: - if s.WrapKeyFn != nil { - var p storeEncryptionKeyReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - return marshalResult(nil), s.WrapKeyFn(ctx, p.Secret, p.Explicit) + if s.WrapKeyFn == nil { + return nil, unknownMethod(req.Method) } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) + var p storeEncryptionKeyReq + if err := unmarshalParams(req.Params, &p); err != nil { + return nil, err + } + return marshalResult(nil), s.WrapKeyFn(ctx, p.Secret, p.Explicit) case MethodUnlock: - if s.UnlockFn != nil { - var p unlockReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - return marshalResult(nil), s.UnlockFn(ctx, p.Secret) + if s.UnlockFn == nil { + return nil, unknownMethod(req.Method) } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) + var p unlockReq + if err := unmarshalParams(req.Params, &p); err != nil { + return nil, err + } + return marshalResult(nil), s.UnlockFn(ctx, p.Secret) case MethodIngestMail: - if s.IngestMailFn != nil { - var p IngestMailReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.IngestMailFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.IngestMailFn) case MethodSwapModel: - if s.SwapModelFn != nil { - var p SwapModelReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.SwapModelFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.SwapModelFn) case MethodDescribeImage: - if s.DescribeImageFn != nil { - var p DescribeImageReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.DescribeImageFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.DescribeImageFn) case MethodCaptureStart: - if s.CaptureStartFn != nil { - var p CaptureStartReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.CaptureStartFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.CaptureStartFn) case MethodCaptureAppend: - if s.CaptureAppendFn != nil { - var p CaptureAppendReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.CaptureAppendFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.CaptureAppendFn) case MethodCaptureStop: - if s.CaptureStopFn != nil { - var p CaptureStopReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.CaptureStopFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - - case MethodCaptureStatus: - if s.CaptureStatusFn != nil { - resp, err := s.CaptureStatusFn(ctx) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.CaptureStopFn) case MethodEnrollSpeaker: - if s.EnrollSpeakerFn != nil { - var p EnrollSpeakerReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - resp, err := s.EnrollSpeakerFn(ctx, p) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirect(ctx, req, s.EnrollSpeakerFn) + case MethodCaptureStatus: + return callDirectNoParams(ctx, req, s.CaptureStatusFn) case MethodListSpeakers: - if s.ListSpeakersFn != nil { - resp, err := s.ListSpeakersFn(ctx) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - - case MethodForgetSpeaker: - if s.ForgetSpeakerFn != nil { - var p ForgetSpeakerReq - if err := unmarshalParams(req.Params, &p); err != nil { - return nil, err - } - if err := s.ForgetSpeakerFn(ctx, p); err != nil { - return nil, err - } - return marshalResult(nil), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) - + return callDirectNoParams(ctx, req, s.ListSpeakersFn) case MethodModelStatus: - if s.ModelStatusFn != nil { - resp, err := s.ModelStatusFn(ctx) - if err != nil { - return nil, err - } - return marshalResult(resp), nil - } - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) + return callDirectNoParams(ctx, req, s.ModelStatusFn) + case MethodForgetSpeaker: + return callDirectVoid(ctx, req, s.ForgetSpeakerFn) } h, ok := methodTable[req.Method] if !ok { - return nil, fmt.Errorf("%w: %s", ErrUnknownMethod, req.Method) + return nil, unknownMethod(req.Method) } return h(ctx, api, req.Params) } +// callDirect runs a daemon-supplied handler that bypasses CoreAPI: unmarshal +// the params, call it, marshal the reply. A nil handler is the capability being +// unconfigured on this box, and the wire says so as an unknown method. +func callDirect[P any, R any](ctx context.Context, req Request, fn func(context.Context, P) (R, error)) (json.RawMessage, error) { + if fn == nil { + return nil, unknownMethod(req.Method) + } + var p P + if err := unmarshalParams(req.Params, &p); err != nil { + return nil, err + } + r, err := fn(ctx, p) + if err != nil { + return nil, err + } + return marshalResult(r), nil +} + +// callDirectNoParams is callDirect for a handler that reads no params. Like +// withoutParams it never touches req.Params. +func callDirectNoParams[R any](ctx context.Context, req Request, fn func(context.Context) (R, error)) (json.RawMessage, error) { + if fn == nil { + return nil, unknownMethod(req.Method) + } + r, err := fn(ctx) + if err != nil { + return nil, err + } + return marshalResult(r), nil +} + +// callDirectVoid is callDirect for a handler with nothing to report back. The +// wire reply is always null. +func callDirectVoid[P any](ctx context.Context, req Request, fn func(context.Context, P) error) (json.RawMessage, error) { + if fn == nil { + return nil, unknownMethod(req.Method) + } + var p P + if err := unmarshalParams(req.Params, &p); err != nil { + return nil, err + } + if err := fn(ctx, p); err != nil { + return nil, err + } + return marshalResult(nil), nil +} + +func unknownMethod(m Method) error { + return fmt.Errorf("%w: %s", ErrUnknownMethod, m) +} + func unmarshalParams(raw json.RawMessage, v any) error { if len(raw) == 0 { raw = []byte("null") From cb3b507ed54895ef5fdbde0828a4e14bdd0938b5 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 6 Aug 2026 01:31:19 +0400 Subject: [PATCH 3/3] ipc: one row mapper for the store adapter's list reads (V-575) Eleven methods repeated the same body: run mapErr over the store's error, allocate a slice of the wire type, convert row by row. mapRows holds it, and each method is now the read plus the conversion it uses. ListProposedRoutines had a conditional copy of ReminderID, which was a nil pointer assigned over a nil pointer whenever it did not fire. It is unconditional now and the result is the same. --- internal/ipc/storeapi.go | 139 ++++++++++----------------------------- 1 file changed, 34 insertions(+), 105 deletions(-) diff --git a/internal/ipc/storeapi.go b/internal/ipc/storeapi.go index 6158d36..21fd7da 100644 --- a/internal/ipc/storeapi.go +++ b/internal/ipc/storeapi.go @@ -77,18 +77,24 @@ func (a *storeAPI) MarkReminder(ctx context.Context, id int64, status string) er return mapErr(a.s.MarkReminder(ctx, id, status)) } -func (a *storeAPI) ListReminders(ctx context.Context, n int) ([]Reminder, error) { - rs, err := a.s.ListReminders(ctx, n) +// mapRows carries a store read's error through mapErr and converts the rows to +// their wire shape. Every list method here is that one shape. +func mapRows[S any, W any](rows []S, err error, conv func(S) W) ([]W, error) { if err != nil { return nil, mapErr(err) } - out := make([]Reminder, len(rs)) - for i, r := range rs { - out[i] = toReminder(r) + out := make([]W, len(rows)) + for i, r := range rows { + out[i] = conv(r) } return out, nil } +func (a *storeAPI) ListReminders(ctx context.Context, n int) ([]Reminder, error) { + rs, err := a.s.ListReminders(ctx, n) + return mapRows(rs, err, toReminder) +} + func (a *storeAPI) RescheduleReminder(ctx context.Context, id int64, now time.Time) error { return mapErr(a.s.RescheduleReminder(ctx, id, now)) } @@ -109,85 +115,48 @@ func (a *storeAPI) RecentOutcomes(ctx context.Context, rule string, n int) ([]st func (a *storeAPI) RecentFacts(ctx context.Context, n int) ([]Fact, error) { fs, err := a.s.RecentFacts(ctx, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]Fact, len(fs)) - for i, f := range fs { - out[i] = toFact(f) - } - return out, nil + return mapRows(fs, err, toFact) } func (a *storeAPI) RecentActiveFactsByKind(ctx context.Context, kind string, n int) ([]Fact, error) { fs, err := a.s.RecentActiveFactsByKind(ctx, store.FactKind(kind), n) - if err != nil { - return nil, mapErr(err) - } - out := make([]Fact, len(fs)) - for i, f := range fs { - out[i] = toFact(f) - } - return out, nil + return mapRows(fs, err, toFact) } func (a *storeAPI) CalendarEvents(ctx context.Context, from, to time.Time) ([]Fact, error) { fs, err := a.s.CalendarEvents(ctx, from, to) - if err != nil { - return nil, mapErr(err) - } - out := make([]Fact, len(fs)) - for i, f := range fs { - out[i] = toFact(f) - } - return out, nil + return mapRows(fs, err, toFact) } func (a *storeAPI) RecentEcosystemTraces(ctx context.Context, n int) ([]EcosystemTrace, error) { trs, err := a.s.RecentEcosystemTraces(ctx, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]EcosystemTrace, len(trs)) - for i, tr := range trs { - out[i] = EcosystemTrace{ + return mapRows(trs, err, func(tr store.EcosystemTrace) EcosystemTrace { + return EcosystemTrace{ ID: tr.ID, Ts: tr.Ts, Service: tr.Service, Operation: tr.Operation, Status: tr.Status, DurationMs: tr.DurationMs, CorrelationID: tr.CorrelationID, CausationID: tr.CausationID, HTTPStatus: tr.HTTPStatus, Fields: tr.Fields, } - } - return out, nil + }) } func (a *storeAPI) RecentNudges(ctx context.Context, n int) ([]Nudge, error) { ns, err := a.s.RecentNudges(ctx, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]Nudge, len(ns)) - for i, ng := range ns { - out[i] = toNudge(ng) - } - return out, nil + return mapRows(ns, err, toNudge) } func (a *storeAPI) DeliveryAttempts(ctx context.Context, status string, n int) ([]DeliveryAttempt, error) { as, err := a.s.ListDeliveryAttempts(ctx, status, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]DeliveryAttempt, len(as)) - for i, at := range as { - out[i] = DeliveryAttempt{ + 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, } if at.HasComplete { t := at.Completed - out[i].Completed = &t + out.Completed = &t } - } - return out, nil + return out + }) } func (a *storeAPI) WriteNote(ctx context.Context, ts time.Time, text string, embedding []float32, source string) (int64, error) { @@ -197,38 +166,17 @@ func (a *storeAPI) WriteNote(ctx context.Context, ts time.Time, text string, emb func (a *storeAPI) QueryNotes(ctx context.Context, embedding []float32, k int) ([]Note, error) { ns, err := a.s.QueryNotes(ctx, embedding, k) - if err != nil { - return nil, mapErr(err) - } - out := make([]Note, len(ns)) - for i, n := range ns { - out[i] = toNote(n) - } - return out, nil + return mapRows(ns, err, toNote) } func (a *storeAPI) RecentNotesFromSource(ctx context.Context, prefix string, n int) ([]Note, error) { ns, err := a.s.RecentNotesFromSource(ctx, prefix, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]Note, len(ns)) - for i, note := range ns { - out[i] = toNote(note) - } - return out, nil + return mapRows(ns, err, toNote) } func (a *storeAPI) RecentNotes(ctx context.Context, n int) ([]Note, error) { ns, err := a.s.RecentNotes(ctx, n) - if err != nil { - return nil, mapErr(err) - } - out := make([]Note, len(ns)) - for i, note := range ns { - out[i] = toNote(note) - } - return out, nil + return mapRows(ns, err, toNote) } func (a *storeAPI) ProposeTool(ctx context.Context, name, utterance, scope string, ts time.Time) (bool, error) { @@ -299,14 +247,7 @@ func (a *storeAPI) DayPlan(ctx context.Context) (DayPlan, error) { func (a *storeAPI) ListTools(ctx context.Context, status string) ([]Tool, error) { ts, err := a.s.ListTools(ctx, status) - if err != nil { - return nil, mapErr(err) - } - out := make([]Tool, len(ts)) - for i, t := range ts { - out[i] = toTool(t) - } - return out, nil + return mapRows(ts, err, toTool) } func (a *storeAPI) DeleteTool(ctx context.Context, name string) error { @@ -334,12 +275,8 @@ func (a *storeAPI) CaptureTask(ctx context.Context, req CaptureTaskReq) (Capture func (a *storeAPI) ListTasks(ctx context.Context, status string) ([]Task, error) { ts, err := a.s.ListTasks(ctx, status) - if err != nil { - return nil, mapErr(err) - } - out := make([]Task, len(ts)) - for i, t := range ts { - out[i] = Task{ + return mapRows(ts, err, func(t store.Task) Task { + return Task{ ID: t.ID, CreatedTs: t.CreatedTs, Text: t.Text, @@ -354,8 +291,7 @@ func (a *storeAPI) ListTasks(ctx context.Context, status string) ([]Task, error) DoneWhen: t.DoneWhen, BlockedOn: t.BlockedOn, } - } - return out, nil + }) } func (a *storeAPI) SetTaskStatus(ctx context.Context, id int64, status string, ts time.Time, by string) error { @@ -379,24 +315,17 @@ func (a *storeAPI) SetTaskFields(ctx context.Context, id int64, doneWhen, blocke func (a *storeAPI) ListProposedRoutines(ctx context.Context) ([]ProposedRoutine, error) { rs, err := a.s.ListProposedRoutines(ctx) - if err != nil { - return nil, mapErr(err) - } - out := make([]ProposedRoutine, len(rs)) - for i, r := range rs { - out[i] = ProposedRoutine{ + return mapRows(rs, err, func(r store.ProposedRoutine) ProposedRoutine { + return ProposedRoutine{ ID: r.ID, Action: r.Action, Object: r.Object, IntervalDays: r.IntervalDays, Status: string(r.Status), CreatedTs: r.CreatedTs.UnixMilli(), + ReminderID: r.ReminderID, } - if r.ReminderID != nil { - out[i].ReminderID = r.ReminderID - } - } - return out, nil + }) } func (a *storeAPI) DismissProposedRoutine(ctx context.Context, id int64) error {