Skip to content

Cases (case-aware workflows)

English translation pending

Full content currently exists only in Russian. Use the language selector at the top of the page → Русский. The skeleton below summarizes the structure; refer to the Russian manual for full text.

A case is a project-scoped business entity (customer, deal, ticket) around which workflow runs are organized. It carries identity_keys (deduplication), properties (free-form JSON), tags, external_refs (Telegram chat, email thread, GDrive folder), and status (active/archived). Axon does not durably store file content — properties.file_sources carries connector-reachable folder references. Canon: CONCEPT-CASE-WORKFLOWS.md §3–§4.

1. What it is and why

Workflow is the unit of execution; case is the unit of context. The same workflow customer_support_triage runs against different cases, and a case aggregates all related runs, artifacts, conversations, and file references. Benefits: per-entity history, deterministic deduplication via identity_keys, context for steps (case.properties, case.external_refs), and case-level connector overrides (D17 middle layer between project binding profile and run-level override).

2. Roles and access

From core/models/auth.py:

Action Permission owner admin manager operator reviewer read_only
Create case create_case
Edit properties / tags / identity_keys edit_case
Archive / unarchive case archive_case
Attach external ref attach_case_external_ref
Delete external ref delete_case_external_ref
Start workflow for case start_workflow

Manager has attach but not delete_case_external_ref — deleting a ref loses context, so it is admin/owner-only. All mutations go through CommandEnvelope handlers in core/api/commands/handlers/cases.py.

3. Where in Console

Sidebar section Cases (URL /{project_id}/cases).

⚠️ Section gated by VITE_CONSOLE_STAGE3_UI_ENABLED. The flag is currently not plumbed through the console Dockerfile build args — the section is hidden in the standard image. Enable at build time with --build-arg VITE_CONSOLE_STAGE3_UI_ENABLED=true (or CONSOLE_STAGE3_UI_ENABLED=true at runtime if the console server supports it). See §8.

Screens: Cases list, Case create, Case detail (Identity / Properties / Tags / External refs / Connector overrides / File sources / Workflow runs / Actions), Case edit. The properties editor is a plain JSON textarea (no schema-driven form builder).

4. Concepts

  • identity_keys — JSON object (must be a JSON object per CHECK cases_identity_keys_object). Deterministic deduplication; GIN-indexed.
  • properties — free-form JSON. Convention fields: properties.file_sources: list[FileSourceRef] and properties.connector_overrides: dict[str, "ci_<ulid>"] (D17).
  • tags — array of strings, UI-only overlay (no runtime semantics).
  • statusactive / archived (CHECK constraint). Archive is a status transition, not a delete.
  • external_refs — 1:N table; kind ∈ {telegram_chat, email_thread, gdrive_folder, ...}.
  • case_aware workflows — flagged in metadata, validated at publish time; Run Wizard requires a case selection.

5. Flows

  1. Create case manually — Cases → + New case → case_type (snake_case) + display_name + identity_keys + tags + properties. Tip: populate identity_keys immediately — empty objects defeat deduplication.
  2. Start workflow for a case — Case detail → Start workflow → Run Wizard with case_id pre-filled and locked. start_workflow pins the final bindings snapshot (project profile → case overrides → run overrides).
  3. Attach external ref — Case detail → External refs → + Attach. For GDrive use the folder URL; parse_gdrive_folder_url() extracts the folder ID.
  4. Archive a case — Case detail → Actions → Archive. Active runs are not auto-cancelled (archive is a business signal, not a system teardown).
  5. Delete external ref — admin+ only, on Case detail → External refs → ⋯ → Delete.

6. Options reference

See the Russian manual §6 for the full field-level reference of app.cases, FileSourceRef, external ref kinds, and the connector_overrides shape. Key invariants enforced at the DB layer: case_id ULID format, case_type regex ^[a-z][a-z0-9_]*$ (1–64 chars), identity_keys JSON object, status ∈ {active, archived}.

7. Lifecycle and operations

Status machine active → archived (archive_case); reverse via update_case_status if business policy allows. Canonical timestamp is the case.archived audit event. Active runs survive archive. Read projection read.case_registry (alembic 035) is rebuildable from app.cases via the outbox consumer. GDPR cascade on project teardown — handled by a sweeper, not by the case handlers.

8. Troubleshooting

Symptom Cause Fix
Cases section invisible VITE_CONSOLE_STAGE3_UI_ENABLED not enabled at console build Rebuild console image with --build-arg VITE_CONSOLE_STAGE3_UI_ENABLED=true.
Delete external ref button missing Lacks delete_case_external_ref (admin/owner only) Escalate or use archive_case instead.
update_case_properties returns 422 «connector_overrides shape invalid» properties.connector_overrides has non-string keys or value not in ci_<26-char ULID> form Use {"slot_name": "ci_01H..."}. Don't confuse with cred_<ulid> (credential, not CI).
start_workflow from case fails with binding_resolver: case_override references unknown CI Case override points at deleted / cross-project / unusable CI Edit Case → Connector overrides → fix or drop the entry.
Event router creates duplicate cases for the same email identity_keys empty or inconsistent Populate identity_keys = {"email": "<addr>"} and align the router/business workflow on the same key.

9. Constraints and invariants

  • project_id NOT NULL. Cross-project leak prevented at DB and handler level.
  • Files are not durably stored; file_sources is a reference only.
  • Mutations only via canonical handlers (no backdoor SQL).
  • Secrets are scanned out of properties by core/security/credential_scan.py.
  • Hard delete of a case is beyond Stage 3 (archive only in the current release).