# Admin Behaviors ## Overview The Admin section is a server-side rendered (SSR) HTMX interface for superuser operations in Integreat. All admin pages require the `admin` user role. Non-admin users are redirected away. The admin area includes dashboards, entity management grids, background job orchestration, audit history, and bulk import tools. **Testing Philosophy** - Prefer unit tests for pure business logic (calculations, validations, transformations) - Use integration tests for database interactions and cross-system flows - Use UI tests only for end-to-end happy paths that touch multiple pages - Every behavior must be user-visible; no tests for implementation details --- ## Testing Patterns ### Pattern: Grid Page Behaviors Most list pages in Integreat follow the same pattern: 1. Fetch IDs via Datomic query with filters 2. Hydrate results via `pull-many` 3. Render table with sortable columns 4. Support selection (individual / all / all-filtered) 5. Action buttons appear conditionally based on permissions and selection state **Test implications:** Validate the query generation, not the rendering. UI tests only need to verify one filter and one sort work end-to-end. ### Pattern: Wizard Behaviors Wizards are multi-step forms with HTMX-driven navigation: 1. Each step is a GET that renders a form fragment 2. Form submissions are POST/PUT with validation 3. Navigation between steps updates the wizard state 4. Final submit creates/updates the entity **Test implications:** Unit test validation logic and state transitions. Integration test the full wizard flow once. UI test only the happy path. ### Pattern: Admin Permission Gates Every admin operation checks: 1. `wrap-client-redirect-unauthenticated` — redirects unauthenticated users to login 2. `wrap-admin` — blocks non-admin authenticated users 3. `assert-can-see-client` — when impersonating, user has access to the client **Test implications:** Integration test each gate independently. UI tests only verify the happy path with an admin user. --- ## Dashboard ### Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 1.1 | It should display a bar chart showing client count at 2 years ago, 1 year ago, and today | UI | [ ] | | 1.2 | It should display a line chart showing Datomic transaction counts per hour over the last 24 hours | UI | [ ] | | 1.3 | It should render the standard admin page layout with the admin-aside-nav sidebar | UI | [ ] | ### Access Control Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 2.1 | It should redirect unauthenticated users to the login page | Integration | [x] | | 2.2 | It should show an authorization failure for authenticated non-admin users | Integration | [x] | --- ## Clients ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.1 | It should display a table with columns: Name, Code, Locations, Emails, Status | UI | [ ] | | 3.2 | It should show location values as pills in the Locations column | UI | [ ] | | 3.3 | It should show email values as pills in the Emails column | UI | [ ] | | 3.4 | It should show lock status as "Locked " with green color when less than 90 days old | UI | [ ] | | 3.5 | It should show lock status with yellow color when between 90 days and 1 year old | UI | [ ] | | 3.6 | It should show lock status with red color when more than 1 year old | UI | [ ] | | 3.7 | It should show "Not locked" in red when no lock date is set | UI | [ ] | | 3.8 | It should show bank account integration status pills in red for failed or unauthorized accounts | UI | [ ] | | 3.9 | It should show a Biweekly Sales PowerQuery button on each row | UI | [ ] | | 3.10 | It should show an Edit button (pencil icon) on each row | UI | [ ] | | 3.11 | It should show a "New Client" button that opens the client wizard | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.1 | It should filter clients by name using case-insensitive substring match | Integration | [x] | | 4.2 | It should filter clients by code using exact match on upper-cased code | Integration | [x] | | 4.3 | It should filter clients by group using exact match on upper-cased group | Integration | [x] | | 4.4 | It should support an "All" or "Only mine" filter to show only clients assigned to the current user | Integration | [x] | | 4.5 | It should trigger HTMX requests with 500ms debounce on filter change and 1000ms debounce on keyup | Integration | [x] | ### Sorting Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 5.1 | It should sort clients by name ascending/descending | Integration | [x] | | 5.2 | It should sort clients by code ascending/descending | Integration | [x] | | 5.3 | It should paginate results with 25 clients per page by default | Integration | [x] | ### Client Wizard Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 6.1 | It should show a multi-step linear wizard with steps: Info, Matches, Contact, Bank Accounts, Integrations, Cash Flow, Other Settings | UI | [ ] | | 6.2 | It should require Name on the Info step | Integration | [ ] | | 6.3 | It should prevent editing the Code field when editing an existing client | UI | [ ] | | 6.4 | It should allow setting a Locked Until date on the Info step | UI | [ ] | | 6.5 | It should show a dynamic grid for adding and removing locations on the Info step | UI | [ ] | | 6.6 | It should allow configuring string match patterns and location match patterns on the Matches step | UI | [ ] | | 6.7 | It should allow entering address fields and email contacts on the Contact step | UI | [ ] | | 6.8 | It should show a sortable card list of existing bank accounts on the Bank Accounts step | UI | [ ] | | 6.9 | It should allow adding cash accounts with nickname, code, financial code, start date, include-in-reports, and visible-for-payment fields | UI | [ ] | | 6.10 | It should allow adding credit card accounts with bank name, account number, and Plaid/Yodlee/Intuit integration selectors | UI | [ ] | | 6.11 | It should allow adding checking accounts with routing number, bank code, and check number fields | UI | [ ] | | 6.12 | It should require a financial code when "Include in Reports" is enabled for a bank account | Unit + Integration | [x] | | 6.13 | It should allow entering a Square auth token and mapping Square locations to client locations on the Integrations step | UI | [ ] | | 6.14 | It should show "No locations found" when the Square location refresh times out after 2 seconds | Integration | [ ] | | 6.15 | It should allow entering Week A/B credits and debits on the Cash Flow step | UI | [ ] | | 6.16 | It should allow selecting feature flags and entering groups on the Other Settings step | UI | [ ] | | 6.17 | It should validate that the client code is unique when creating a new client | Unit + Integration | [x] | | 6.18 | It should upper-case group values on save | Unit | [x] | | 6.19 | It should flash the updated row in the grid and close the modal after a successful save | UI | [ ] | | 6.20 | It should reindex the client in Solr after a successful save | Integration | [x] | ### Biweekly Sales PowerQuery Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 7.1 | It should generate 6 saved queries per client (sales summary, category, expected deposits, tenders, refunds, cash drawer shifts) | Integration | [ ] | | 7.2 | It should open a modal with copy-to-clipboard buttons for Excel Power Query M-code | UI | [ ] | --- ## Accounts ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 8.1 | It should display a table with columns: Code, Name, Type, Location | UI | [ ] | | 8.2 | It should show the account type as a colored pill | UI | [ ] | | 8.3 | It should show an Edit button on each row | UI | [ ] | | 8.4 | It should show a "New Account" button | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 9.1 | It should filter accounts by name using case-insensitive substring match on upper-cased name | Integration | [x] | | 9.2 | It should filter accounts by code using exact numeric match | Integration | [x] | | 9.3 | It should filter accounts by type: All, Dividend, Asset, Equity, Liability, Expense, Revenue, or None | Integration | [x] | ### Sorting Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 10.1 | It should sort accounts by code, name, type, or location ascending/descending | Integration | [x] | | 10.2 | It should default sort by upper-cased numeric code | Integration | [x] | ### Account Dialog Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 11.1 | It should show a modal dialog with a live-updating header displaying the numeric code and name | UI | [ ] | | 11.2 | It should require a numeric code when creating a new account | Integration | [x] | | 11.3 | It should hide the numeric code field when editing an existing account | UI | [ ] | | 11.4 | It should require a name and account type | Integration | [x] | | 11.5 | It should allow setting Invoice Allowance, Vendor Allowance, and Applicability as dropdown enums | UI | [ ] | | 11.6 | It should show a Client Overrides grid with client typeahead and override name | UI | [ ] | | 11.7 | It should validate that no client appears more than once in the Client Overrides grid | Unit + Integration | [x] | | 11.8 | It should validate that the numeric code is unique when creating a new account | Unit + Integration | [x] | | 11.9 | It should reindex the account and all client overrides in Solr after a successful save | Integration | [x] | --- ## Vendors ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 12.1 | It should display a table with columns: Name, Email, Default Account | UI | [ ] | | 12.2 | It should show an "Unused" pill in red when a vendor has 0 clients | UI | [ ] | | 12.3 | It should show a "Used by N clients" pill in primary color when a vendor is assigned to clients | UI | [ ] | | 12.4 | It should show a "Used N times" pill in secondary color when a vendor has transaction usage | UI | [ ] | | 12.5 | It should show an Edit button on each row that opens the vendor wizard | UI | [ ] | | 12.6 | It should show a "Merge" button for merging vendors | UI | [ ] | | 12.7 | It should show a "New Vendor" button | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 13.1 | It should filter vendors by name using case-insensitive substring match on upper-cased name | Integration | [x] | | 13.2 | It should filter vendors by visibility: All, Only hidden, or Only global | Integration | [x] | ### Vendor Wizard Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 14.1 | It should show a multi-step wizard with steps: Info, Terms, Account, Address, Legal | UI | [ ] | | 14.2 | It should require a name of at least 3 characters on the Info step | Unit + Integration | [x] | | 14.3 | It should allow toggling a "Print As" alias on the Info step | UI | [ ] | | 14.4 | It should show a "Hidden" checkbox on the Info step visible only to admins | UI | [ ] | | 14.5 | It should allow setting terms in days and a grid of client-specific terms overrides on the Terms step | UI | [ ] | | 14.6 | It should allow configuring a list of clients for automatically paid when due on the Terms step | UI | [ ] | | 14.7 | It should allow selecting a default account via typeahead on the Account step | UI | [ ] | | 14.8 | It should show an Account Overrides grid where account typeahead is scoped by selected client | Integration | [x] | | 14.9 | It should allow entering address fields with a 2-character state and 5-character zip on the Address step | UI | [ ] | | 14.10 | It should allow entering a legal entity name OR first/middle/last name, TIN, TIN type, and 1099 type on the Legal step | UI | [ ] | | 14.11 | It should validate that terms override clients are unique with no duplicates | Unit + Integration | [x] | | 14.12 | It should reindex the vendor name and hidden flag in Solr after a successful save | Integration | [x] | ### Vendor Merge Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 15.1 | It should open a modal with Source Vendor and Target Vendor selectors | UI | [ ] | | 15.2 | It should validate that the source and target vendors are different | Unit + Integration | [x] | | 15.3 | It should retract all references to the source vendor and assert them as the target vendor on merge | Integration | [x] | | 15.4 | It should show a success notification after a successful merge | UI | [ ] | --- ## Rules ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 16.1 | It should display a table with columns: Client, Bank Account, Description, Amount, Note | UI | [ ] | | 16.2 | It should show a group pill in the Client column when the rule applies to a client group | UI | [ ] | | 16.3 | It should show amount gte/lte filters as pills in the Amount column | UI | [ ] | | 16.4 | It should show Delete, Execute, and Edit row action buttons | UI | [ ] | | 16.5 | It should show a "New Transaction Rule" button | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 17.1 | It should filter rules by vendor using an entity typeahead | Integration | [x] | | 17.2 | It should filter rules by note using case-insensitive regex match | Integration | [x] | | 17.3 | It should filter rules by description using case-insensitive substring match | Integration | [x] | | 17.4 | It should filter rules by client group using exact upper-cased match | Integration | [x] | ### Transaction Rule Wizard Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 18.1 | It should show a two-step wizard: Edit then Test | UI | [ ] | | 18.2 | It should require a description regex pattern of at least 3 characters on the Edit step | Unit + Integration | [x] | | 18.3 | It should allow toggling optional filters for Client, Client Group, Bank Account, Amount range, and Day of Month range | UI | [ ] | | 18.4 | It should scope the bank account selector to the selected client | Integration | [x] | | 18.5 | It should allow assigning a vendor, configuring account grids, and setting approval status as outcomes | UI | [ ] | | 18.6 | It should derive account location from the account's fixed location, client locations, or "Shared" | Unit | [x] | | 18.7 | It should validate that account percentages sum to exactly 100% | Unit + Integration | [x] | | 18.8 | It should validate that the selected bank account belongs to the selected client | Unit + Integration | [x] | | 18.9 | It should validate that the rule location matches the account's fixed location when one is set | Unit + Integration | [x] | | 18.10 | It should show up to 15 matching transactions on the Test step with client, bank, date, and description | Integration | [x] | | 18.11 | It should display a badge showing the total match count with "99+" when 99 or more transactions match | UI | [ ] | ### Rule Execution Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 19.1 | It should open a dialog with checkbox-selectable transactions that match the rule and are unapproved | UI | [ ] | | 19.2 | It should include only transactions on or after the client's locked-until date | Integration | [x] | | 19.3 | It should allow selecting all matching transactions or individual transactions | UI | [ ] | | 19.4 | It should apply rule coding to each selected transaction | Integration | [x] | | 19.5 | It should update the Solr index after rule execution | Integration | [x] | | 19.6 | It should show a notification reading "Successfully coded X of Y transactions!" after execution | UI | [ ] | ### Rule Deletion Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 20.1 | It should show a confirmation dialog before deleting a rule | UI | [ ] | | 20.2 | It should retract the rule entity from the database on confirmation | Integration | [x] | | 20.3 | It should fade out the row with a "live-removed" animation after deletion | UI | [ ] | --- ## Jobs ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 21.1 | It should display a table with columns: Start time, End time, Duration, Name, Status | UI | [ ] | | 21.2 | It should show status values as running, pending, succeeded, or failed | UI | [ ] | | 21.3 | It should display ECS tasks filtered by the INTEGREAT_JOB environment variable | Integration | [ ] | | 21.4 | It should show a "Run job" button | UI | [ ] | ### Job Start Dialog Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 22.1 | It should show a job type dropdown with options: Yodlee Import, Yodlee Account Import, Intuit Import, Plaid Import, Bulk Journal Import, Square Import, Register Invoice Import, Upsert recent ezcater orders, Load Historical Square Sales, Export Backup | UI | [ ] | | 22.2 | It should show a dynamic subform with an S3 URL path input for Bulk Journal Import and Register Invoice Import | UI | [ ] | | 22.3 | It should show a client typeahead and days input (1-120) for Load Historical Square Sales | UI | [ ] | | 22.4 | It should prevent starting a job that is already running | Integration | [ ] | | 22.5 | It should launch an ECS Fargate Spot task on submit | Integration | [ ] | --- ## History ### Search Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 23.1 | It should allow searching for an entity by numeric Datomic entity ID | UI | [ ] | | 23.2 | It should show an error notification when the entity ID cannot be parsed as a Long | Integration | [ ] | ### Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 24.1 | It should display a history table with columns: Date, User, Field, From value, To value | UI | [ ] | | 24.2 | It should format date values in local format | Unit | [ ] | | 24.3 | It should display large integers greater than 1 million as clickable links to that entity's history | UI | [ ] | | 24.4 | It should display nil values as "(none)" | Unit | [ ] | | 24.5 | It should allow clicking an entity ID to load that entity's history inline | Integration | [ ] | | 24.6 | It should show a Snapshot link that opens an inspector displaying all entity attributes | UI | [ ] | | 24.7 | It should show all history rows without pagination | Integration | [ ] | ### Inspector Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 25.1 | It should display a card showing all attributes of an entity at the current database value | UI | [ ] | | 25.2 | It should allow clicking entity IDs within the inspector to recurse into that entity's history | Integration | [ ] | --- ## Imports ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 26.1 | It should display a table with columns: Date, Source, Status, User, Imported, Pre-existing, Suppressed | UI | [ ] | | 26.2 | It should show an external link on each row to transactions filtered by import batch | UI | [ ] | | 26.3 | It should show a "New Import" button | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 27.1 | It should filter import batches by date range | Integration | [ ] | | 27.2 | It should filter import batches by source | Integration | [ ] | ### Sorting Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 28.1 | It should sort import batches by date, source, status, or user | Integration | [ ] | | 28.2 | It should paginate results with 25 import batches per page by default | Integration | [ ] | --- ## Excel Invoices ### Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 29.1 | It should display a single-page form with a large textarea for tab-separated invoice data | UI | [ ] | | 29.2 | It should show sample data as a placeholder in the textarea | UI | [ ] | ### Import Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 30.1 | It should parse tab-separated rows with columns: raw-date, vendor-name, check, location, invoice-number, amount, client-name, bill-entered, bill-rejected, added-on, exported-on, account-numeric-code | Unit | [ ] | | 30.2 | It should resolve the client by code or name | Integration | [ ] | | 30.3 | It should resolve the vendor by exact case-sensitive name match | Integration | [ ] | | 30.4 | It should resolve the account by numeric code | Integration | [ ] | | 30.5 | It should group rows into new, existing, and error categories | Unit | [ ] | | 30.6 | It should create a paid invoice with zero outstanding balance and a cash transaction when the check type is "Cash" | Integration | [ ] | | 30.7 | It should create an unpaid invoice with full outstanding balance when the check type is not "Cash" | Integration | [ ] | | 30.8 | It should display results as pills showing imported count, extant count, and vendors not found with hover tooltip | UI | [ ] | | 30.9 | It should display an error grid for rows that failed validation | UI | [ ] | --- ## Sales Summaries ### Grid Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 31.1 | It should display a table with columns: Client, Date, Debits, Credits | UI | [ ] | | 31.2 | It should hide the Client column when only one client is selected | UI | [ ] | | 31.3 | It should show each debit/credit item with category, amount, and a red "missing account" pill when no account is mapped | UI | [ ] | | 31.4 | It should show a total row with a green pill when debits equal credits, or a red pill when unbalanced | UI | [ ] | | 31.5 | It should show an Edit button on each row | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 32.1 | It should filter sales summaries by date range | Integration | [ ] | | 32.2 | It should scope results to the user's valid clients | Integration | [ ] | ### Edit Wizard Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 33.1 | It should display a grid of sales summary items with Category, Account, Debits, and Credits columns | UI | [ ] | | 33.2 | It should allow editing the category, account, and debit/credit amounts for manual items | UI | [ ] | | 33.3 | It should allow removing manual items from the grid | UI | [ ] | | 33.4 | It should display auto-generated items with read-only category and amount but editable account | UI | [ ] | | 33.5 | It should scope the account typeahead to the client and filter for invoice-purpose accounts | Integration | [ ] | | 33.6 | It should update the live total row and unbalanced row when amounts change | UI | [ ] | | 33.7 | It should validate that each item has exactly one of credit or debit, not both | Unit + Integration | [ ] | | 33.8 | It should validate that total debits equal total credits before saving | Unit + Integration | [ ] | | 33.9 | It should update ledger-mapped account assignments and flag manual items on save | Integration | [ ] | --- ## Cross-Cutting Behaviors ### Admin-Only Access Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 34.1 | It should redirect unauthenticated users to the login page on all admin routes | Integration | [ ] | | 34.2 | It should show an authorization failure for authenticated non-admin users on all admin routes | Integration | [ ] | | 34.3 | It should require admin role for all mutating admin handlers | Integration | [ ] | ### Audit History Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 35.1 | It should record the admin user who performed each mutating operation via the `:audit/user` attribute | Integration | [ ] | | 35.2 | It should write all mutating operations through `audit-transact` or `audit-transact-batch` | Integration | [ ] | | 35.3 | It should allow querying all changes to an entity from Datomic's history database on the History page | Integration | [ ] | ### Impersonation Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 36.1 | It should allow admin users to select a client from the global client selector to filter admin grids | UI | [ ] | | 36.2 | It should respect the selected client when filtering the Clients, Transaction Rules, and Sales Summaries grids | Integration | [ ] | ### Form Validation Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 37.1 | It should enforce form structure via Malli schemas | Unit | [ ] | | 37.2 | It should validate query params, route params, and form params via `wrap-schema-enforce` | Integration | [ ] | | 37.3 | It should re-render dialogs with field-level validation errors on 400 responses | Integration | [ ] | ### Solr Indexing Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 38.1 | It should reindex Solr documents after creating or updating a client | Integration | [ ] | | 38.2 | It should reindex Solr documents after creating or updating a vendor or account | Integration | [ ] | --- ## Test Data Requirements | Entity | Requirements | |--------|-------------| | **Users** | Admin user with `:user/role :user-role/admin`; non-admin user with `:user/role :user-role/user` | | **Clients** | Client with name, code, and locations; client with locked-until in past and future; client with bank accounts (cash, credit, checking); client with Square auth token; client with feature flags and groups | | **Accounts** | Account with unique numeric code, name, and type; account with fixed location; account with client overrides | | **Vendors** | Vendor with name and default account; vendor with terms overrides and account overrides; hidden and non-hidden vendors; vendor with 1099 legal entity info | | **Transaction Rules** | Rule with description pattern, client, bank account, amount gte/lte; rule with account assignments summing to 100%; unapproved transactions matching rule criteria; transactions before and after client's locked-until date | | **Background Jobs** | ECS cluster with task definitions containing `INTEGREAT_JOB` env var, or mock ECS responses | | **Import Batches** | Import batch entities with date, source, and status | | **Sales Summaries** | Sales summary with ledger-mapped items; both manual and auto-generated items | ## Existing Tests to Preserve - `test/clj/auto_ap/integration/graphql/users.clj` — User role and permission tests - `test/clj/auto_ap/integration/graphql/accounts.clj` — Account search and override tests - `test/clj/auto_ap/integration/graphql/vendors.clj` — Vendor management tests - `test/clj/auto_ap/integration/graphql/transaction_rules.clj` — Rule matching and execution tests ## Dependencies - Datomic (primary store, history, pull API) - HTMX (frontend interactivity) - Alpine.js (modal state, conditional visibility) - Chartist (dashboard charts) - Solr (search indexing) - ECS API (background jobs) - Malli (schema validation) - Bidi (routing)