Skip to Content
Internal docs are powered by Nextra Docs Theme.
SystemsPlatformAPI2Rails to API2 migrationPackagesFirst-party tag mutations

Scope and ownership

ENG-2722  moves four Rails HTTP actions into conversation-tag-writes.ts and routes the UI’s three existing tag mutation call sites through API2. There is no existing first-party catalog-delete caller. This package is distinct from ENG-1711’s completed by-name agent/workflow tag implementation.

HTTP entrypointPrevious actionNative implementation
POST /tagsTagsController#createcreateCatalogTag
DELETE /tags/:idTagsController#destroydeleteCatalogTag
POST /conversations/:conversation_id/tagsConversationTagsController#createapplyCatalogTag
DELETE /conversations/:conversation_id/tags/:idConversationTagsController#destroyremoveCatalogTag

The new paths do not dispatch jobs or Rails mutation requests. Rails authentication on uncached API2 sessions and the shared public.tags, public.conversation_tags, conversation, and membership tables remain explicit dependencies. The existing GET tag selection branch remains pending separate read rollout evidence. Rails controller removal and internal/external consumers are outside this bounded transfer.

Parity and validation

  • Global cookie session authentication runs before all four routes. API keys do not bypass session authentication for these paths. Route guards require assignments:create and reject browser origins outside the configured CORS allowlist or the request’s own origin, including opaque null origins.
  • Catalog and join queries are tenant scoped. Conversation mutations load the current database membership and reuse the native ConversationPolicy::Scope equivalent, including group/partner/team restrictions and employee self-access.
  • HTTP application resolves a tag prefix ID; it never interprets an invalid or foreign ID as a name. Normalized catalog creation remains idempotent and returns 200. Apply returns 201 with join timestamps; deletion returns an empty 204.
  • An omitted summary preserves the existing summary and timestamps. Blank/null clears it. Long summaries truncate at 2,000 Unicode characters with an ellipsis. Tag names enforce the Rails 255-character limits.
  • Unique indexes deduplicate catalog and join creation. Transactional catalog deletion removes dependent joins before the tag. Catalog-row locks serialize native apply/remove/delete; existing join rows are also locked while updating summaries to serialize with legacy deletes. FK enforcement protects other writers. DB fixtures load both unique index definitions from Rails db/structure.sql. At 23:24 UTC September 2, read-only US/EU pg_index checks verified that both exact indexes exist, are unique and valid, and match the conflict targets: tags(organization_id, normalized_name) and conversation_tags(conversation_id, tag_id).
  • UI optimistic snapshots, error rollback, and conversation/catalog invalidation remain unchanged. Route DB tests cover denial, foreign IDs, restricted scope, self-access, normalization, duplicate requests, and deletion rollback. The production app composition test verifies that every path requires a session.

No schema migration, feature flag, provider queue, or capacity change is required. The EU provider backlog has no demonstrated dependency on these mutations.

Rollout and rollback

Merge only after required checks and reviews pass. Main’s standard automatic deployment activates these API2 server routes and the first-party client switch; observe the actual server and UI revisions independently before marking deployed. The migrated routes use the existing production origin allowlist, which must include the regional first-party UI origin as it does for existing API2 reads.

Observe tag_mutation_completed structured logs by organization, method, path, and status, existing request logs, and Sentry for authorization regressions, 5xx responses, or missing tag changes. No tag names or summaries are logged. Verify at least one real successful create/apply/remove workflow in each region and inspect a complete 24-hour request/error window before assigning production bake credit. Absence of traffic is missing evidence, not a successful bake.

Rollback by reverting this package’s server/client revision through the normal reviewed main deployment. The shared tables remain Rails compatible; no data repair or queue replay is needed. Keep code-ready, merged, deployed, and baked states separate in the migration ledger.

Last updated on