Skip to content

feat(AGX1-274): record task creator identity and FGAC migration safety#246

Draft
asherfink wants to merge 1 commit into
mainfrom
asher.fink/agx1-274-task-dual-write
Draft

feat(AGX1-274): record task creator identity and FGAC migration safety#246
asherfink wants to merge 1 commit into
mainfrom
asher.fink/agx1-274-task-dual-write

Conversation

@asherfink
Copy link
Copy Markdown

@asherfink asherfink commented May 21, 2026

Related work

This stack lands per-task FGAC for AGX1-264. Merge order: 2b → 2 → 3 (scale-agentex assumes agentex-auth understands cancel before sending it).

Stream Repo PR Purpose
2b scaleapi/agentex #353 agentex-auth per-account routing + cancel op
2 (this PR) scaleapi/scale-agentex #246 task creator audit columns + migration safety
3 scaleapi/scale-agentex #249 per-RPC operation rewire + 404/403 wrap

Parent epic: AGX1-264. Follow-ups bundled in AGX1-291.

Summary

  • Adds passive audit columns creator_user_id / creator_service_account_id to the tasks table, populated from the request principal on AgentTaskService.create_task (best-effort — see caveat below).
  • Adds a CHECK ((creator_user_id IS NULL) OR (creator_service_account_id IS NULL)) to enforce at-most-one creator type at the DB layer (constraint name: ck_tasks_at_most_one_creator).
  • Adds the partial indexes ix_tasks_creator_user_id and ix_tasks_creator_service_account_id (CREATE INDEX CONCURRENTLY) for future "tasks created by X" lookups.

Migration safety

  • ALTER TABLE ... ADD CONSTRAINT ... NOT VALID + ALTER TABLE ... VALIDATE CONSTRAINT — splits the operation so the brief ACCESS EXCLUSIVE lock doesn't have to wait on an existence scan. tasks is a high-write table; even a quick CHECK addition without NOT VALID would queue behind in-flight transactions and block readers until released.
  • Revision hash a1f73ada66c5 unchanged; migration_history.txt, downgrade(), and the ORM-side CheckConstraint in orm.py all untouched (constraint name + predicate identical).
  • Indexes created CONCURRENTLY in an autocommit_block.
  • migration_history.txt also records finalize_spans_task_id (a9959ebcbe98) as a drive-by — that migration was already on main but its history line wasn't recorded.

Audit-trail caveat

Creator attribution is best-effort: tasks created outside an HTTP request context (Temporal activities, background workers, any code path that constructs AgentTaskService without request.state.principal_context) will leave both columns NULL. This is intentional for v1 — the CHECK constraint allows both-NULL, and the integration test test_no_resolvable_creator_leaves_both_columns_null exercises this path.

What changed

  • database/migrations/alembic/versions/2026_05_21_1508_add_task_creator_columns_a1f73ada66c5.py (new): NOT VALID-pattern migration.
  • src/adapters/orm.py: declarative CheckConstraint mirroring the DB constraint.
  • src/domain/entities/tasks.py: new optional fields on TaskEntity.
  • src/domain/services/task_service.py: _principal_field helper (handles dict/pydantic principal shape from the authn proxy); create_task reads creator_user_id / creator_service_account_id from principal context.
  • Tests: new integration test test_task_audit_columns.py exercises a real Postgres via testcontainers (creator population, mutual-exclusion CHECK, both-NULL allowed). Existing unit/integration tests updated for the new constructor signature.

Test plan

  • migration_lint.py — clean.
  • Ruff + ruff-format clean across all touched files.
  • test_task_audit_columns.py — 7/7 pass locally via testcontainers against real Postgres; runs in the CI unit + integration suite on every push.
  • Manual: deploy to staging, confirm \d tasks shows the new columns + constraint + indexes, run ANALYZE if needed.

@asherfink asherfink force-pushed the asher.fink/agx1-274-task-dual-write branch from 13fe4b2 to 7486e5a Compare May 26, 2026 20:22
@asherfink asherfink changed the title feat(AGX1-274): dual-write tasks to spark-authz behind FGAC_TASKS_DUAL_WRITE flag feat(AGX1-274): record task creator identity and FGAC migration safety May 26, 2026
…n creation

Adds two nullable creator-audit columns to the tasks table — creator_user_id
and creator_service_account_id — populated from the principal context at
create time. A CHECK constraint (ck_tasks_one_creator) enforces that at most
one is set.

This replaces the earlier dual-write draft: grants are already issued
unconditionally via grant_with_retry in agents_acp_use_case.py:239, and
per-account rollout routing belongs in agentex-auth (private), not in this
public Apache-2.0 codebase.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant