Two triggers: every task.* event names its task in subject, and task.created cites a human.input event and a requirement id. The task ref pattern is tightened on snapshots, decisions and subjects. Runs on Node 24.21.0 and 26.8.1 match apart from the version line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
4.0 KiB
Slice 1 prototype, v3a (lead decision 56, Q3)
Darkwing, 2026-10-04. Q3 asked whether a trigger or the broker enforces
two rules from Dewey's S5 questions. My answer is a trigger for both,
plus the broker setting the fields correctly in the first place. That's
how v2 and v3 already treat the credential.* and task.missing
bodies. Both rules depend only on the row being inserted and on rows
already in events, so SQL can check them. A broker bug that drops a
subject or the request id then fails at the insert. Without the trigger,
the bad row would only show up later as a gap in a trail. The limit is
the same as before: a process running as the same user can drop a
trigger, and the open-time digest only notices that afterwards.
schema-v3a.sql is a full schema that replaces v3. It isn't a
migration. The v1, v2 and v3 files are unchanged. diff schema-v3.sql schema-v3a.sql shows every change:
- New trigger
events_task_subject. Everytask.*event must carry a task ref insubject. That coverstask.created,task.assigned,task.state,task.closed,task.changed.external,task.conflictandtask.missing. - New trigger
events_task_created_body. Atask.createdbody carriesrequest, the id of ahuman.inputevent in the same business that is already inevents. It also carriesrequirement, a PRD id shaped likeREQ-TASK-1. Addendum A section 3 made thetask.createdevent the authoritative holder of the requirement id, so I check it in the same trigger. Checking that the id exists in the PRD version the business file names stays with the broker, because the schema can't read the PRD. Sage can drop the requirement half if it's unwanted. - The task ref pattern is now
vikunja:<project>/<task>, with both ids positive integers, no leading zero and exactly one slash. v3'sGLOB 'vikunja:[0-9]*/[0-9]*'letvikunja:3/41xthrough. The pattern applies totask_snapshots.task_ref, todecisions.task_refwhen it isn't null (v3 didn't check that column at all), and to task event subjects. events_task_missing_bodyno longer checks the subject, becauseevents_task_subjectdoes. Its message changed to match.
The broker enforces the rest of Q3's first rule. action.allowed,
action.refused and review.* events are sometimes about one task, but
the kind doesn't say when. A refused task.create, for example, has no
task yet. The broker sets subject on those whenever the action names a
task.
proto-v3a.mjs is proto-v3.mjs with these changes: the header, the
temp directory prefix, the schema file name, a new "task events name
their task" section, a task_ref vikunja:3/41x snapshot case, a second
unknown-kind case with a subject, and the append-only check's event row
(h-1 instead of e-2, since e-2 is now a refused insert).
Results: proto-v3a-node24.txt (Node 24.21.0 in the node:24 image,
no network, the directory mounted read-only) and proto-v3a-node26.txt
(Node 26.8.1 on the host). Both use SQLite 3.53.4, and the outputs
differ only in the version line. Tables 8, triggers 35, views 4. The new
refusals:
task.statewith no subject,PROJ-41,vikunja:3/41x,vikunja:03/41orvikunja:3/4/1;task.createdwith no request, a request naming an unknown event, a credential event, ahuman.inputfrom another business or a number; with no requirement,REQ-task-1orREQ-TASK-0; or with no subject;- a decision with
task_refvikunja:3/41x, and a snapshot with the same ref.
task.created citing h-1 and REQ-TASK-1 is accepted, and the trail
view from h-1 finds it. The v3 cases give the same results as before,
with one exception. "Unknown kind task.deleted" is now refused by the
subject trigger, because BEFORE INSERT triggers run before the table's
CHECK. The new case "with a subject" shows the kind CHECK still refuses
it.
Mutant: the same script against schema-v3a.sql without the EXISTS
clause accepts the unknown-event, credential-event and other-business
cases, and changes nothing else. It ran in ~/darkwing-scratch/v3a,
with output in mutant.txt there.