fix: add ResolutionFlow service account to own default tree steps in library

Default/system trees had no author_id (NULL), causing a NOT NULL violation
when syncing steps to step_library.created_by on publish.

- Add is_service_account flag to users table (migration 4f4137ce)
- Add service_account.py: idempotent ensure_service_account() creates
  noreply@resolutionflow.com with unusable password on startup
- Cache service account ID on app.state at lifespan startup
- Add get_service_account_id() FastAPI dep (returns None in tests)
- sync_steps_from_tree: resolve author_id or service_account_id as created_by
- create_tree: set author_id=service_account_id for is_default trees
- Migration 1490781700bc: backfill author_id on 31 existing default trees

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-25 23:17:04 -05:00
parent 0002f75232
commit c2b3937e86
8 changed files with 221 additions and 6 deletions

View File

@@ -109,14 +109,22 @@ async def sync_steps_from_tree(
tree_id: UUID,
tree_type: str,
tree_structure: dict,
author_id: UUID,
author_id: Optional[UUID],
account_id: Optional[UUID],
is_public: bool,
service_account_id: Optional[UUID] = None,
) -> int:
"""Upsert step library entries from a published tree.
Returns the number of steps synced.
For default/system trees that have no author_id, pass service_account_id
so that created_by is set to the ResolutionFlow service account.
"""
resolved_author_id = author_id or service_account_id
if not resolved_author_id:
return 0
now = datetime.now(timezone.utc)
extracted = list(extract_steps_for_sync(tree_structure, tree_type))
@@ -157,7 +165,7 @@ async def sync_steps_from_tree(
"title": step_data["title"],
"step_type": step_data["step_type"],
"content": json.dumps(step_data["content"]),
"created_by": str(author_id) if author_id else None,
"created_by": str(resolved_author_id),
"account_id": str(account_id) if account_id else None,
"visibility": visibility,
"source_tree_id": str(tree_id),