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.
This commit is contained in:
2026-08-06 01:30:41 +04:00
parent 70b32af8a7
commit 43dc487113
3 changed files with 55 additions and 131 deletions
+6 -9
View File
@@ -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.
+5 -7
View File
@@ -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
+44 -115
View File
@@ -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)