Files
stack/agents/darkwing/work/slice1-proto/proto-v3a-notes.md
T
jason.woltjeandClaude Opus 5.5 29daa48216 docs(slice1): schema v3a, task event rules (lead decision 56, Q3)
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]>
2026-10-04 22:02:31 -05:00

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. Every task.* event must carry a task ref in subject. That covers task.created, task.assigned, task.state, task.closed, task.changed.external, task.conflict and task.missing.
  • New trigger events_task_created_body. A task.created body carries request, the id of a human.input event in the same business that is already in events. It also carries requirement, a PRD id shaped like REQ-TASK-1. Addendum A section 3 made the task.created event 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's GLOB 'vikunja:[0-9]*/[0-9]*' let vikunja:3/41x through. The pattern applies to task_snapshots.task_ref, to decisions.task_ref when it isn't null (v3 didn't check that column at all), and to task event subjects.
  • events_task_missing_body no longer checks the subject, because events_task_subject does. 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.state with no subject, PROJ-41, vikunja:3/41x, vikunja:03/41 or vikunja:3/4/1;
  • task.created with no request, a request naming an unknown event, a credential event, a human.input from another business or a number; with no requirement, REQ-task-1 or REQ-TASK-0; or with no subject;
  • a decision with task_ref vikunja: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.