Add the day plan she can recite when asked (#128)
The plan answers "какие планы на сегодня?" by putting one day in order: calendar events (with #126's ambient provenance carried through and hedged), pending reminders, and one line per morning routine that still has items outstanding. "что дальше?" trims what has already passed. It lives in internal/morning, not in a parallel system, because it is the same question the checklist asks at a different scale — the routine knows what is missing from a window, the plan knows what the whole day holds, and both read the same facts and the same idea of "today". BuildPlan is pure; tickLoop.dayPlan is the impure half that reads the store. It is not a nag. Nothing here fires, schedules or announces: the plan is built only when asked, over IPC (day_plan) or on the existing /morning page. Unprompted delivery stays with the morning nudge and the dispatcher's policy. The query source sits before "calendar" in querySources because both match "…на сегодня" and the plan's matcher is the more specific one; IsDayPlanQuery matches whole words so "планёрка" (a meeting) is not read as a request for the plan, and refuses any utterance naming another day, since the plan is built for the clock's own day only. Verified: make build and make test both exit 0; new tests cover plan ordering, the checklist-only-what-is-left rule, other-day rejection, the RU rendering against the persona checks, rest-of-day trimming, the source ordering, and the matcher's refusals.
This commit is contained in:
@@ -306,6 +306,14 @@ type CoreAPI interface {
|
||||
// TickTrace.
|
||||
MorningStatus(ctx context.Context) ([]MorningRoutineStatus, error)
|
||||
|
||||
// DayPlan returns today's ordered plan — calendar events, pending
|
||||
// reminders and any morning checklist still outstanding (see
|
||||
// internal/morning.BuildPlan) — plus the spoken RU rendering of it.
|
||||
// Read-only: asking for the plan never dispatches or schedules anything.
|
||||
// The store adapter returns an error (the plan needs the daemon's routine
|
||||
// config) — same shape as TickTrace and MorningStatus.
|
||||
DayPlan(ctx context.Context) (DayPlan, error)
|
||||
|
||||
// Chat routes a text utterance through the reactive handler's core path
|
||||
// (router → dialogue → action → replier) and returns the reply text.
|
||||
// No audio or stt/tts — for text channels (mavweb, telegram).
|
||||
@@ -359,6 +367,26 @@ type MorningRoutineStatus struct {
|
||||
Items []MorningRoutineItem `json:"items"`
|
||||
}
|
||||
|
||||
// DayPlanItem — one line of the day plan. Kind is "event", "reminder" or
|
||||
// "checklist"; Uncertain marks an item whose provenance is below a full
|
||||
// calendar read (a meeting relayed off a phone notification), so a UI can hedge
|
||||
// the same way the spoken form does.
|
||||
type DayPlanItem struct {
|
||||
At time.Time `json:"at"`
|
||||
Text string `json:"text"`
|
||||
Kind string `json:"kind"`
|
||||
Uncertain bool `json:"uncertain,omitempty"`
|
||||
}
|
||||
|
||||
// DayPlan — the plan for one calendar day. Spoken is the RU sentence maven
|
||||
// says when asked, rendered core-side so the voice reply and the web view can
|
||||
// never drift apart.
|
||||
type DayPlan struct {
|
||||
Date time.Time `json:"date"`
|
||||
Items []DayPlanItem `json:"items"`
|
||||
Spoken string `json:"spoken"`
|
||||
}
|
||||
|
||||
// storeEncryptionKeyReq — passkey credential public key for wrapping the store
|
||||
// encryption key at enrollment time. Called by mavweb after RegisterFinish.
|
||||
type storeEncryptionKeyReq struct {
|
||||
|
||||
@@ -71,6 +71,7 @@ var readOnlyMethods = map[Method]bool{
|
||||
MethodListProposedRoutines: true,
|
||||
MethodTickTrace: true,
|
||||
MethodMorningStatus: true,
|
||||
MethodDayPlan: true,
|
||||
}
|
||||
|
||||
// Dial connects to a core socket at path and returns a Client. The module
|
||||
@@ -458,6 +459,14 @@ func (c *Client) MorningStatus(ctx context.Context) ([]MorningRoutineStatus, err
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *Client) DayPlan(ctx context.Context) (DayPlan, error) {
|
||||
var p DayPlan
|
||||
if err := c.call(ctx, MethodDayPlan, nil, &p); err != nil {
|
||||
return DayPlan{}, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *Client) RevertFact(ctx context.Context, key string) (int64, error) {
|
||||
var result struct {
|
||||
NewID int64 `json:"new_id"`
|
||||
|
||||
@@ -211,6 +211,10 @@ func (a *storeAPI) MorningStatus(ctx context.Context) ([]MorningRoutineStatus, e
|
||||
return nil, errors.New("store: morning status not available via direct store API")
|
||||
}
|
||||
|
||||
func (a *storeAPI) DayPlan(ctx context.Context) (DayPlan, error) {
|
||||
return DayPlan{}, errors.New("store: day plan not available via direct store API")
|
||||
}
|
||||
|
||||
func (a *storeAPI) ListTools(ctx context.Context, status string) ([]Tool, error) {
|
||||
ts, err := a.s.ListTools(ctx, status)
|
||||
if err != nil {
|
||||
@@ -729,6 +733,9 @@ var methodTable = map[Method]handlerFunc{
|
||||
// MorningStatus intentionally has no nil→[]T{} normalization here — the
|
||||
// pre-table arm marshaled api.MorningStatus's result as-is (a nil slice
|
||||
// serializes as JSON null), and this preserves that exact wire shape.
|
||||
MethodDayPlan: withoutParams(func(ctx context.Context, api CoreAPI) (DayPlan, error) {
|
||||
return api.DayPlan(ctx)
|
||||
}),
|
||||
MethodMorningStatus: withoutParams(func(ctx context.Context, api CoreAPI) ([]MorningRoutineStatus, error) {
|
||||
return api.MorningStatus(ctx)
|
||||
}),
|
||||
|
||||
@@ -113,6 +113,9 @@ func (UnimplementedCoreAPI) TickTrace(ctx context.Context) (TickTrace, error) {
|
||||
func (UnimplementedCoreAPI) MorningStatus(ctx context.Context) ([]MorningRoutineStatus, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
func (UnimplementedCoreAPI) DayPlan(ctx context.Context) (DayPlan, error) {
|
||||
return DayPlan{}, ErrNotImplemented
|
||||
}
|
||||
func (UnimplementedCoreAPI) Chat(ctx context.Context, text string) (string, error) {
|
||||
return "", ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ const (
|
||||
MethodRevertFact Method = "revert_fact"
|
||||
MethodTickTrace Method = "tick_trace"
|
||||
MethodMorningStatus Method = "morning_status"
|
||||
MethodDayPlan Method = "day_plan"
|
||||
MethodChat Method = "chat"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user