Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
This commit was merged in pull request #121.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
-- 0013_week_plan_revision: every edit of a week's plan — a swap or a
|
||||
-- stored draft — raises its revision. A draft_week job reads the revision
|
||||
-- before draft_week runs and stores its week only while the plan still
|
||||
-- has it, so a draft never overwrites an edit made while the AI ran.
|
||||
|
||||
ALTER TABLE week_plans ADD COLUMN revision INTEGER NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1,43 @@
|
||||
-- 0014_rotation_review_name: a Rotation check decided drop ("Not for us")
|
||||
-- keeps the name its dinner had when the household dropped it. The dinner
|
||||
-- can be renamed, even once archived, so the passed names every later
|
||||
-- batch refuses are read from here, never from the dinner, as 0012 does
|
||||
-- for a Pass. Only a drop has a name. A check dropped before this takes
|
||||
-- its dinner's name now.
|
||||
-- SQLite cannot add a column with a CHECK that existing drops would fail
|
||||
-- before they are filled in, so the table is rebuilt as 0012 does: copy
|
||||
-- the rows aside, drop the table, create it again, copy the rows back and
|
||||
-- drop the copy. Nothing references rotation_reviews.
|
||||
|
||||
CREATE TABLE rotation_reviews_copy AS SELECT * FROM rotation_reviews;
|
||||
|
||||
DROP TABLE rotation_reviews;
|
||||
|
||||
CREATE TABLE rotation_reviews (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
dinner_id TEXT NOT NULL REFERENCES dinners (id),
|
||||
plan_date TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ('pending', 'decided')),
|
||||
decision TEXT CHECK (decision IN ('keep', 'drop', 'not_made')),
|
||||
name TEXT CHECK (name <> ''), -- the dinner's name when dropped
|
||||
decided_at TEXT,
|
||||
updated_by TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE (dinner_id, plan_date),
|
||||
CHECK ((decision IS 'drop') = (name IS NOT NULL))
|
||||
);
|
||||
|
||||
INSERT INTO rotation_reviews (
|
||||
id, dinner_id, plan_date, status, decision, name, decided_at, updated_by, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
rotation_reviews_copy.id, rotation_reviews_copy.dinner_id, rotation_reviews_copy.plan_date,
|
||||
rotation_reviews_copy.status, rotation_reviews_copy.decision,
|
||||
CASE WHEN rotation_reviews_copy.decision = 'drop' THEN dinners.name END,
|
||||
rotation_reviews_copy.decided_at, rotation_reviews_copy.updated_by,
|
||||
rotation_reviews_copy.created_at, rotation_reviews_copy.updated_at
|
||||
FROM rotation_reviews_copy
|
||||
JOIN dinners ON dinners.id = rotation_reviews_copy.dinner_id;
|
||||
|
||||
DROP TABLE rotation_reviews_copy;
|
||||
@@ -1,11 +1,17 @@
|
||||
-- Reads for the AI household context block (internal/ai/harness).
|
||||
|
||||
-- name: ListRecentDeclinedNames :many
|
||||
-- The names the most recently declined suggestions were suggested under,
|
||||
-- newest first; renaming their dinners does not change them.
|
||||
SELECT name
|
||||
FROM suggestions
|
||||
WHERE status = 'declined'
|
||||
-- name: ListRecentPassedNames :many
|
||||
-- The most recent names the household turned down, newest first, as
|
||||
-- ListPassedNames has them.
|
||||
SELECT name FROM (
|
||||
SELECT name, decided_at, id
|
||||
FROM suggestions
|
||||
WHERE status = 'declined'
|
||||
UNION ALL
|
||||
SELECT name, decided_at, id
|
||||
FROM rotation_reviews
|
||||
WHERE decision = 'drop'
|
||||
)
|
||||
ORDER BY decided_at DESC, id DESC
|
||||
LIMIT ?;
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
-- name: GetWeekPlan :one
|
||||
-- The plan of the week starting week_start. Returns sql.ErrNoRows when
|
||||
-- there is none.
|
||||
SELECT * FROM week_plans
|
||||
WHERE week_start = ?;
|
||||
|
||||
-- name: ListPlanDays :many
|
||||
-- The days of plan plan_id with their dinners, Monday first. A day with
|
||||
-- no dinner is left out.
|
||||
SELECT plan_days.date, plan_days.is_new, sqlc.embed(dinners)
|
||||
FROM plan_days
|
||||
JOIN dinners ON dinners.id = plan_days.dinner_id
|
||||
WHERE plan_days.plan_id = ?
|
||||
ORDER BY plan_days.date;
|
||||
|
||||
-- name: CreateWeekPlan :exec
|
||||
INSERT INTO week_plans (id, week_start, status, ai_note, created_at, updated_at)
|
||||
VALUES (?, ?, 'draft', ?, ?, ?);
|
||||
|
||||
-- name: RedraftWeekPlan :exec
|
||||
-- Records a new draft over plan id: its status and note, raising its
|
||||
-- revision.
|
||||
UPDATE week_plans
|
||||
SET status = ?, ai_note = ?, revision = revision + 1, updated_at = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: PutPlanDay :exec
|
||||
-- Plans dinner_id on date of plan_id, keeping the day's calendar event id
|
||||
-- when the day exists.
|
||||
INSERT INTO plan_days (id, plan_id, date, dinner_id, is_new, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (plan_id, date) DO UPDATE
|
||||
SET dinner_id = excluded.dinner_id, is_new = excluded.is_new, updated_at = excluded.updated_at;
|
||||
|
||||
-- name: SwapPlanDay :execrows
|
||||
-- Plans dinner_id on an existing day of plan_id; a date the plan does not
|
||||
-- have is left as it is (0 rows).
|
||||
UPDATE plan_days
|
||||
SET dinner_id = ?, is_new = ?, updated_at = ?
|
||||
WHERE plan_id = ? AND date = ?;
|
||||
|
||||
-- name: MarkWeekPlanEdited :exec
|
||||
-- Records an edit of plan id, raising its revision: a synced plan becomes
|
||||
-- changed, any other keeps its status.
|
||||
UPDATE week_plans
|
||||
SET status = CASE WHEN status = 'synced' THEN 'changed' ELSE status END,
|
||||
revision = revision + 1,
|
||||
updated_at = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: ListDraftPool :many
|
||||
-- The dinners a draft of the week starting week_start may plan: every
|
||||
-- Regular, and every Quest log dinner no other plan for the week starting
|
||||
-- from_week_start or later has on any day.
|
||||
SELECT * FROM dinners
|
||||
WHERE status = 'regular'
|
||||
OR (status = 'quest_log' AND NOT EXISTS (
|
||||
SELECT 1 FROM plan_days
|
||||
JOIN week_plans ON week_plans.id = plan_days.plan_id
|
||||
WHERE plan_days.dinner_id = dinners.id
|
||||
AND week_plans.week_start >= sqlc.arg(from_week_start)
|
||||
AND week_plans.week_start <> sqlc.arg(week_start)
|
||||
))
|
||||
ORDER BY normalized_name, id;
|
||||
|
||||
-- name: ListWeekPlanDinnerIDs :many
|
||||
-- The ids of the dinners planned in the week starting week_start, Monday
|
||||
-- first.
|
||||
SELECT plan_days.dinner_id
|
||||
FROM plan_days
|
||||
JOIN week_plans ON week_plans.id = plan_days.plan_id
|
||||
WHERE week_plans.week_start = ? AND plan_days.dinner_id IS NOT NULL
|
||||
ORDER BY plan_days.date;
|
||||
|
||||
-- name: GetActiveDraftJob :one
|
||||
-- The job of kind queued or running for the week starting week_start,
|
||||
-- with whether its payload has confirm. StartDraft enqueues a draft only
|
||||
-- when there is none, so there is at most one. Returns sql.ErrNoRows when
|
||||
-- there is none.
|
||||
SELECT id, CAST(json_extract(payload, '$.confirm') = 1 AS INTEGER) AS confirmed
|
||||
FROM jobs
|
||||
WHERE kind = sqlc.arg(kind)
|
||||
AND status IN ('queued', 'running')
|
||||
AND json_extract(payload, '$.week_start') = sqlc.arg(week_start)
|
||||
ORDER BY id
|
||||
LIMIT 1;
|
||||
@@ -0,0 +1,66 @@
|
||||
-- name: ListTriedCandidates :many
|
||||
-- The ids of the dinners with status quest_log planned on date, on a plan
|
||||
-- of any status, that have no Rotation check for date yet.
|
||||
SELECT DISTINCT dinners.id
|
||||
FROM plan_days
|
||||
JOIN dinners ON dinners.id = plan_days.dinner_id
|
||||
WHERE plan_days.date = sqlc.arg(date)
|
||||
AND dinners.status = 'quest_log'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM rotation_reviews
|
||||
WHERE rotation_reviews.dinner_id = dinners.id
|
||||
AND rotation_reviews.plan_date = plan_days.date
|
||||
)
|
||||
ORDER BY dinners.id;
|
||||
|
||||
-- name: MarkDinnerTried :execrows
|
||||
-- Moves a dinner still in the Quest log to tried; a dinner with any other
|
||||
-- status is left as it is (0 rows).
|
||||
UPDATE dinners
|
||||
SET status = 'tried', updated_at = ?
|
||||
WHERE id = ? AND status = 'quest_log';
|
||||
|
||||
-- name: CreateRotationReview :exec
|
||||
INSERT INTO rotation_reviews (id, dinner_id, plan_date, status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, 'pending', ?, ?);
|
||||
|
||||
-- name: ListPendingRotationReviews :many
|
||||
-- The pending Rotation checks whose dinner is still tried, oldest plan
|
||||
-- date first, with their dinners.
|
||||
SELECT rotation_reviews.plan_date, sqlc.embed(dinners)
|
||||
FROM rotation_reviews
|
||||
JOIN dinners ON dinners.id = rotation_reviews.dinner_id
|
||||
WHERE rotation_reviews.status = 'pending' AND dinners.status = 'tried'
|
||||
ORDER BY rotation_reviews.plan_date, rotation_reviews.id;
|
||||
|
||||
-- name: GetPendingRotationReview :one
|
||||
-- The pending Rotation check of dinner_id. MarkTried makes one only for a
|
||||
-- dinner in the Quest log, which it moves to tried, so a dinner has at
|
||||
-- most one. Returns sql.ErrNoRows when there is none.
|
||||
SELECT * FROM rotation_reviews
|
||||
WHERE dinner_id = ? AND status = 'pending';
|
||||
|
||||
-- name: CountRotationReviews :one
|
||||
-- How many Rotation checks dinner_id has had, pending or decided.
|
||||
SELECT COUNT(*) FROM rotation_reviews WHERE dinner_id = ?;
|
||||
|
||||
-- name: DecideRotationReview :execrows
|
||||
-- Records the household's decision on a pending Rotation check, with the
|
||||
-- dinner's name for a drop and NULL otherwise; one already decided is left
|
||||
-- as it is (0 rows).
|
||||
UPDATE rotation_reviews
|
||||
SET status = 'decided',
|
||||
decision = sqlc.arg(decision),
|
||||
name = sqlc.narg(name),
|
||||
decided_at = sqlc.arg(now),
|
||||
updated_by = sqlc.arg(updated_by),
|
||||
updated_at = sqlc.arg(now)
|
||||
WHERE id = sqlc.arg(id) AND status = 'pending';
|
||||
|
||||
-- name: MoveTriedDinner :execrows
|
||||
-- Moves a dinner still tried to status (regular, archived or quest_log)
|
||||
-- by a Rotation check decision, recording updated_by; its origin is kept.
|
||||
-- A dinner no longer tried is left as it is (0 rows).
|
||||
UPDATE dinners
|
||||
SET status = sqlc.arg(status), updated_by = sqlc.arg(updated_by), updated_at = sqlc.arg(updated_at)
|
||||
WHERE id = sqlc.arg(id) AND status = 'tried';
|
||||
@@ -86,12 +86,20 @@ FROM suggestions
|
||||
JOIN dinners ON dinners.id = suggestions.dinner_id
|
||||
WHERE suggestions.status = 'pending' AND dinners.status = 'suggested';
|
||||
|
||||
-- name: ListDeclinedSuggestionNames :many
|
||||
-- The name every declined suggestion was suggested under, oldest decision
|
||||
-- first; renaming its dinner does not change it.
|
||||
SELECT name
|
||||
FROM suggestions
|
||||
WHERE status = 'declined'
|
||||
-- name: ListPassedNames :many
|
||||
-- Every name the household turned down, oldest decision first: the name
|
||||
-- each declined suggestion was suggested under, which renaming its dinner
|
||||
-- does not change, and the name each dinner a Rotation check dropped
|
||||
-- ("Not for us") had when dropped, which renaming it does not change.
|
||||
SELECT name FROM (
|
||||
SELECT name, decided_at, id
|
||||
FROM suggestions
|
||||
WHERE status = 'declined'
|
||||
UNION ALL
|
||||
SELECT name, decided_at, id
|
||||
FROM rotation_reviews
|
||||
WHERE decision = 'drop'
|
||||
)
|
||||
ORDER BY decided_at, id;
|
||||
|
||||
-- name: DecideSuggestion :execrows
|
||||
|
||||
Reference in New Issue
Block a user