Per IMPLEMENTATION-ORDER.md 1.5: a nullable actor_child_id column
records which panel profile was tapped; admin events stay NULL.
- schema.py: completion_events table body extracted to
COMPLETION_EVENTS_TABLE_SQL; the new column carries a FK to
children(id) and the actor-pair coherence is extended to a second
CHECK: 'user' events require actor_user_id and forbid
actor_child_id, 'panel' events require actor_child_id and forbid
actor_user_id.
- migrations.py: migration 6 rebuilds legacy-shaped completion_events
via the canonical procedure (CHECKs cannot be added to an existing
table), backfilling each legacy panel row to its own child_id
(pre-D-008 panel completions were always the tapped child) via
INSERT..SELECT — no UPDATE or DELETE path exists, so the append-only
guarantee is untouched; ids preserved; PRAGMA foreign_key_check
verified in-transaction.
- dao_instances.py: append() takes actor_child_id, validates both
actor shapes (panel requires it, user forbids it) and the tapped
child's existence under the connection lock; the record and column
list grow accordingly.
- tests: coherence CHECK matrix (5 bad shapes), unknown
actor_child_id FK, panel/user round-trips at DDL and DAO layers,
migration-6 backfill test on a frozen v2-shape seed; the
completion_events leak guard gains justified INSERT exemptions for
the migration layer (UPDATE/DELETE stay forbidden everywhere).