jevstrudel.git / worker / migrations / 0003_listening.sql

Listening (src/listening.ts reads and writes all three, through src/listening-store.ts). Anonymous: no address, visitor or account. A performance keeps the day it was recorded (not the time), so history can be read by date; nothing else says who or when.

reactions: the 🔥/😴 buttons of every listener, a count per (song, section, reaction). section is the name the song's form gives it (walk()), or the section's number in a song without a form; the Worker cannot check it against the song, so the page shows and sends Jev only the names its song's form has.

11CREATE TABLE reactions (
12  song TEXT NOT NULL CHECK (length(song) BETWEEN 3 AND 129),
13  section TEXT NOT NULL CHECK (length(section) BETWEEN 1 AND 64),
14  reaction TEXT NOT NULL CHECK (reaction IN ('fire', 'sleep')),
15  n INTEGER NOT NULL CHECK (n > 0),
16  PRIMARY KEY (song, section, reaction)
17) STRICT;

performances: one play of a song, as Jev arranged it, for a replay link (/songs/<song>/?performance=<id>) and for Jev's decision history. code_hash is the SHA-256 of the code that played, so a replay of code changed since can say so. form_jev/form_question name the choice a walk() form is over, when the song has one.

24CREATE TABLE performances (
25  id TEXT PRIMARY KEY CHECK (length(id) = 22),
26  song TEXT NOT NULL CHECK (length(song) BETWEEN 3 AND 129),
27  code_hash TEXT NOT NULL CHECK (length(code_hash) = 64),
28  model TEXT NOT NULL,
29  every INTEGER NOT NULL CHECK (every > 0),
30  ended TEXT NOT NULL CHECK (ended IN ('finished', 'stopped')),
31  jevs INTEGER NOT NULL CHECK (jevs > 0),
32  form_jev INTEGER CHECK (form_jev >= 0 AND form_jev < jevs),
33  form_question TEXT,
34  day TEXT NOT NULL CHECK (day GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'),
35  CHECK ((form_jev IS NULL) = (form_question IS NULL))
36) STRICT;
37CREATE INDEX performances_by_song ON performances (song);

performance_segments: Jev's decision history, one row per segment per jev() of a performance. answers is the JSON the segment played, keyed by question: { choice | score | noul, confidence?, probabilities?, sampled?, top?, fallback?, forced?, absent? } (jevCore.mjs's answers). section is the form's section this segment plays and after_section the one before it, when there is a form: so "what does Jev pick after drop2d" is WHERE after_section = 'drop2d', and json_each(answers) reaches every question.

47CREATE TABLE performance_segments (
48  performance TEXT NOT NULL REFERENCES performances (id) ON DELETE CASCADE,
49  jev INTEGER NOT NULL CHECK (jev >= 0),
50  segment INTEGER NOT NULL CHECK (segment >= 0),
51  section TEXT,
52  after_section TEXT,
53  status TEXT NOT NULL CHECK (status IN ('opening', 'answered', 'fallback')),
54  ms INTEGER CHECK (ms >= 0),
55  from_cycle INTEGER CHECK (from_cycle >= 0),
56  answers TEXT NOT NULL CHECK (json_valid(answers) AND json_type(answers) = 'object'),
57  PRIMARY KEY (performance, jev, segment),
58  CHECK (segment > 0 OR after_section IS NULL)
59) STRICT;
60CREATE INDEX performance_segments_by_section ON performance_segments (section, after_section);