-- open-time schema check
check  after create -> match
-- decisions.blocking
refuse raise without blocking -> NOT NULL constraint failed: decisions.blocking
refuse raise with blocking 2 -> CHECK constraint failed: blocking IN (0,1)
refuse raise blocking without task_ref -> a blocking decision cites the task it blocks
ok     raise blocking gated with task_ref
ok     raise non-blocking gated
ok     raise blocking cross-role
view   urgent_inbox -> [{"id":"d-3","task_ref":"vikunja:3/41"}]
ok     resolve d-3 with A
view   urgent_inbox after resolve -> []
-- events: closed kinds and the new kinds
refuse unknown kind task.deleted -> a task event names its task in subject
refuse unknown kind task.deleted with a subject -> CHECK constraint failed: kind IN ( 'session.launched', 'session.ended', 'action.allowed', 
ok     credential.expiring vikunja coder
ok     credential.expired vikunja coder
ok     credential.changed gitea pm
refuse credential.changed without instance -> credential events name a service and a role instance
refuse credential.expiring service github -> credential events name a service and a role instance
refuse task.missing without reason -> task.missing carries a reason, and the new project when moved
refuse task.missing reason deleted -> task.missing carries a reason, and the new project when moved
refuse task.missing without subject -> a task event names its task in subject
refuse task.missing moved without project -> task.missing carries a reason, and the new project when moved
ok     task.missing not-found
ok     task.missing moved to project 9
-- task events name their task (lead decision 56, Q3)
refuse task.state without subject -> a task event names its task in subject
refuse task.state subject PROJ-41 -> a task event names its task in subject
refuse task.state subject vikunja:3/41x -> a task event names its task in subject
refuse task.state subject vikunja:03/41 -> a task event names its task in subject
refuse task.state subject vikunja:3/4/1 -> a task event names its task in subject
ok     task.state subject vikunja:3/41
ok     human.input from the cli
ok     human.input in another business
refuse task.created without request -> task.created cites the human.input that asked for it and a requirement id
refuse task.created request names an unknown event -> task.created cites the human.input that asked for it and a requirement id
refuse task.created request names a credential event -> task.created cites the human.input that asked for it and a requirement id
refuse task.created request from another business -> task.created cites the human.input that asked for it and a requirement id
refuse task.created request as a number -> task.created cites the human.input that asked for it and a requirement id
refuse task.created without requirement -> task.created cites the human.input that asked for it and a requirement id
refuse task.created requirement REQ-task-1 -> task.created cites the human.input that asked for it and a requirement id
refuse task.created requirement REQ-TASK-0 -> task.created cites the human.input that asked for it and a requirement id
refuse task.created without subject -> a task event names its task in subject
ok     task.created cites h-1 and REQ-TASK-1
refuse decision with task_ref vikunja:3/41x -> CHECK constraint failed: task_ref IS NULL OR (task_ref GLOB 'vikunja:[1-9]*/[1-9]*' AND NO
view   e-3 is -> [{"kind":"credential.expiring"}]
view   trail from h-1 -> [{"kind":"task.created","subject":"vikunja:3/50"}]
ok     digest.sent
refuse launch.revoked by pm run -> only the human revokes or restores launching
ok     launch.revoked by human
view   launch_state -> [{"business":"mosaic-stack","state":"revoked"}]
ok     launch.restored by human
view   launch_state -> [{"business":"mosaic-stack","state":"allowed"}]
-- task_snapshots
refuse self without role and run -> CHECK constraint failed: (source = 'self') = (role IS NOT NULL AND run IS NOT NULL)
refuse poll with a role -> CHECK constraint failed: (source = 'self') = (role IS NOT NULL AND run IS NOT NULL)
refuse poll without via -> CHECK constraint failed: (source = 'poll') = (via IS NOT NULL AND read_at IS NOT NULL)
refuse poll without read_at -> CHECK constraint failed: (source = 'poll') = (via IS NOT NULL AND read_at IS NOT NULL)
refuse self with via -> CHECK constraint failed: (source = 'poll') = (via IS NOT NULL AND read_at IS NOT NULL)
refuse unknown via webhook -> CHECK constraint failed: via IN ('board','cursor','task','reconcile')
refuse fields without bucket -> CHECK constraint failed: json_type(fields, '$.bucket') IS 'integer' OR (source IS 'poll' A
refuse bucket as text -> CHECK constraint failed: json_type(fields, '$.bucket') IS 'integer' OR (source IS 'poll' A
refuse gone on a board read -> CHECK constraint failed: json_type(fields, '$.bucket') IS 'integer' OR (source IS 'poll' A
refuse gone on self -> CHECK constraint failed: json_type(fields, '$.bucket') IS 'integer' OR (source IS 'poll' A
refuse bad task_ref -> CHECK constraint failed: task_ref GLOB 'vikunja:[1-9]*/[1-9]*' AND NOT substr(task_ref, 9)
refuse task_ref vikunja:3/41x -> CHECK constraint failed: task_ref GLOB 'vikunja:[1-9]*/[1-9]*' AND NOT substr(task_ref, 9)
refuse bad digest -> CHECK constraint failed: length(digest) = 64 AND NOT digest GLOB '*[^0-9a-f]*'
ok     self X by coder, response :02
ok     cursor X sent :04 (unchanged)
view   external after cursor X -> []
ok     cursor Y sent :06, same second (person edit)
view   external after cursor Y -> [{"task_ref":"vikunja:3/41","via":"cursor"}]
ok     self Z by coder (move to in-review), response :10
ok     stale board read sent :09 shows Y
view   external after self Z, stale board read -> []
ok     stale cursor read, updated 11:59:00
view   external after stale cursor read -> []
ok     board read sent :30 agrees with Z
view   external after board agrees -> []
ok     person moves to blocked, updated unchanged, board sent :40
view   external after person's move -> [{"task_ref":"vikunja:3/41","via":"board"}]
ok     board read on vikunja:3/42 with no self row
ok     cursor read on vikunja:3/44, done
view   tasks_open -> [{"task_ref":"vikunja:3/41","bucket":14},{"task_ref":"vikunja:3/42","bucket":11}]
ok     tombstone for vikunja:3/42 (GET 404)
view   tasks_open after tombstone -> [{"task_ref":"vikunja:3/41","bucket":14}]
view   external, all -> [{"task_ref":"vikunja:3/41","via":"board"},{"task_ref":"vikunja:3/42","via":"task"},{"task_ref":"vikunja:3/44","via":"cursor"}]
-- append-only on every table
refuse meta UPDATE -> meta is append-only
refuse meta DELETE -> meta is append-only
refuse meta INSERT OR REPLACE -> meta is append-only
refuse events UPDATE -> events is append-only
refuse events DELETE -> events is append-only
refuse events INSERT OR REPLACE -> events is append-only
refuse role_claims UPDATE -> role_claims is append-only
refuse role_claims DELETE -> role_claims is append-only
refuse role_claims INSERT OR REPLACE -> role_claims is append-only
refuse decisions UPDATE -> decisions is append-only
refuse decisions DELETE -> decisions is append-only
refuse decisions INSERT OR REPLACE -> decisions is append-only
refuse decision_events UPDATE -> decision_events is append-only
refuse decision_events DELETE -> decision_events is append-only
refuse decision_events INSERT OR REPLACE -> decision_events is append-only
refuse messages UPDATE -> messages is append-only
refuse messages DELETE -> messages is append-only
refuse messages INSERT OR REPLACE -> messages is append-only
refuse deliveries UPDATE -> deliveries is append-only
refuse deliveries DELETE -> deliveries is append-only
refuse deliveries INSERT OR REPLACE -> deliveries is append-only
refuse task_snapshots UPDATE -> task_snapshots is append-only
refuse task_snapshots DELETE -> task_snapshots is append-only
refuse task_snapshots INSERT OR REPLACE -> task_snapshots is append-only
-- tamper: drop a guard, reopen
check  on reopen -> match
check  after DROP TRIGGER -> MISMATCH
node 26.8.1 | sqlite 3.53.4 | journal: wal | tables: 8 | triggers: 35 | views: 4
