Implements IMPLEMENTATION-ORDER.md §1.3 (decision D-008). Stacks on PR #45 (1.1 rename) — its commit is included until that PR merges.
Changes
schema.py: quest_definitions loses child_id; new quest_definition_assignees (definition_id, child_id) composite PK + FKs; table body extracted to QUEST_DEFINITIONS_TABLE_SQL shared with the rebuild migration.
migrations.py: callable migrations own their transaction and stamp via the new stamp_schema_version helper (the rebuild needs PRAGMA foreign_keys OFF/ON around it). Migration 3 creates the assignees table and rebuilds a pre-D-008 definitions table when child_id is present: canonical SQLite procedure, PRAGMA foreign_key_check verified inside the transaction before stamping. Migration 2 converted to the new contract.
dao_rules.py: create() takes assignee_child_ids applied atomically; set_assignee replaced by add_assignee (idempotent, active-child validated under the connection lock), remove_assignee, list_assignees; list_by_child joins through assignees.
dao_instances.py: upsert validates the child is an assignee of the definition.
Implements IMPLEMENTATION-ORDER.md §1.3 (decision D-008). Stacks on PR #45 (1.1 rename) — its commit is included until that PR merges.
## Changes
- **schema.py**: quest_definitions loses child_id; new quest_definition_assignees (definition_id, child_id) composite PK + FKs; table body extracted to QUEST_DEFINITIONS_TABLE_SQL shared with the rebuild migration.
- **migrations.py**: callable migrations own their transaction and stamp via the new stamp_schema_version helper (the rebuild needs PRAGMA foreign_keys OFF/ON around it). Migration 3 creates the assignees table and rebuilds a pre-D-008 definitions table when child_id is present: canonical SQLite procedure, PRAGMA foreign_key_check verified inside the transaction before stamping. Migration 2 converted to the new contract.
- **dao_rules.py**: create() takes assignee_child_ids applied atomically; set_assignee replaced by add_assignee (idempotent, active-child validated under the connection lock), remove_assignee, list_assignees; list_by_child joins through assignees.
- **dao_instances.py**: upsert validates the child is an assignee of the definition.
- **tests**: assignees DDL shape/constraints; assignment API rewrite; v2→v3 rebuild migration tests (definition + instance rows survive, child_id gone, FK check clean, fresh insert works).
## Verification
uv run pytest: 788 passed.
Closes task 5cc267f6 (feature 14, task 1.3).
Per design/IMPLEMENTATION-ORDER.md 1.1 and decision D-007: the domain
model takes the quest vocabulary pre-release. SCHEMA_V1 now creates
quest_definitions/quest_instances directly; DAO classes, records and
error messages follow (QuestDefinitionsDao, QuestInstancesDao). The
migration runner gains migration 2, an async-callable step that detects
surviving task_* tables on dev machines and ALTER TABLE RENAMES them
inside the migration transaction, with FK clauses rewritten by SQLite.
Migration 1's content rewrite is legal because nothing was ever
shipped; dev databases stamped version 1 ride migration 2 up to 2.
Per IMPLEMENTATION-ORDER.md 1.3: a quest_definition_assignees table
(definition_id, child_id) with a composite primary key and FKs to
quest_definitions and children replaces the single child_id column on
quest_definitions.
- schema.py: definitions DDL loses child_id; new assignees table joins
SCHEMA_V1_STATEMENTS; the definitions table body is extracted to
QUEST_DEFINITIONS_TABLE_SQL so the rebuild migration cannot drift
from the canonical shape.
- migrations.py: callable migrations now own their transaction and
stamp via stamp_schema_version(database, version), because the
rebuild must bracket itself with PRAGMA foreign_keys OFF/ON. New
migration 3 creates the assignees table and, when a pre-D-008
quest_definitions still carries child_id, rebuilds the table via
SQLite's canonical procedure (create, copy, move assignees, drop,
rename), verifies PRAGMA foreign_key_check inside the transaction,
and stamps. Migration 2 converted to the new callable contract.
- dao_rules.py: QuestDefinitionsDao.create drops the child_id argument
and applies assignee_child_ids atomically; set_assignee is replaced
by add_assignee (idempotent, active-child validated under the
connection lock), remove_assignee and list_assignees; list_by_child
reads through the assignees table.
- dao_instances.py: upsert validates the child against
quest_definition_assignees instead of the definitions column.
- tests: schema shape tests for the assignees table (columns, PK, FKs,
duplicate/unknown constraints); DAO tests rewritten for the new
assignment API; migration tests for the v2 -> v3 rebuild (rows and
instances survive, child_id gone, foreign_key_check clean).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements IMPLEMENTATION-ORDER.md §1.3 (decision D-008). Stacks on PR #45 (1.1 rename) — its commit is included until that PR merges.
Changes
Verification
uv run pytest: 788 passed.
Closes task 5cc267f6 (feature 14, task 1.3).