868ae4cbfd
To test, go to https://twenty-applications.twenty.com/settings/applications/6aa2ca76-fdbe-456d-89ab-c622452ef055 ## After <img width="1512" height="697" alt="image" src="https://github.com/user-attachments/assets/5b17f94e-6e0e-400a-8291-a39ad796e423" /> ## What Adds the design spec for the next version of the `twenty-last-contact` public app, extending it from a single `lastContactAt` date column to the three-column experience in the app's cover image on the All People view: 1. **Last contact by** — the team member who last interacted with the person (`ACTOR` field). 2. **Last contact** — the existing `lastContactAt` field, unchanged. 3. **Last contact item** — the email or meeting that was the last contact, as a clickable record (`MORPH_RELATION` → message | calendarEvent). All three columns always describe the same single most-recent interaction (atomic "newer wins" update). This PR contains the **design doc only** — `docs/superpowers/specs/2026-06-29-last-contact-by-and-item-design.md`. Implementation follows. ## Why The app today only answers *when* you last talked to someone. These fields also answer *who* on your team and *through which* email/meeting, matching the product vision in the cover. ## Key design decisions - **`lastContactBy` is an ACTOR**, with the team member resolved from the interaction's participants (`messageParticipant` / `calendarEventParticipant` both carry `workspaceMemberId` + `workspaceMember`). - **No provider (Gmail/Outlook) logo.** That data lived on `connectedAccount`, which v2.7 (`drop-connected-account-standard-object`) removed from the app-queryable workspace schema. Confirmed acceptable; the actor still shows the member + an email/calendar source. - **`lastContactItem` is a MORPH_RELATION** following the SDK pattern used by `attachment` / `noteTarget` / `taskTarget` (shared `morphId`, one field per target, reverse relation on each target object). ## Reviewer notes - **Load-bearing open risk** documented in the spec: how to *write* a morph relation through the app's GraphQL API — no app in the repo writes morph yet. The plan starts with a spike on this; if morph writes aren't supported from an app, the fallback is two nullable `RELATION` fields (`lastContactMessage` / `lastContactCalendarEvent`). - No code/behavior change yet — safe to merge or hold as the design of record. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22308?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
51 lines
2.1 KiB
YAML
51 lines
2.1 KiB
YAML
name: Spawn Twenty App Dev Test
|
|
description: >
|
|
Starts a Twenty all-in-one test instance (server, worker, database, redis)
|
|
using the twentycrm/twenty-app-dev Docker image on port 2021.
|
|
The server is available at http://localhost:2021 with seeded demo data.
|
|
|
|
inputs:
|
|
twenty-version:
|
|
description: 'Twenty Docker Hub image tag for twenty-app-dev (e.g., "latest" or "v1.20.0").'
|
|
required: false
|
|
default: 'latest'
|
|
|
|
outputs:
|
|
server-url:
|
|
description: 'URL where the Twenty test server can be reached'
|
|
value: http://localhost:2021
|
|
api-key:
|
|
description: 'API key (type: API_KEY) for the seeded Twenty dev workspace'
|
|
value: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc
|
|
|
|
runs:
|
|
using: 'composite'
|
|
steps:
|
|
- name: Start twenty-app-dev-test container
|
|
shell: bash
|
|
run: |
|
|
docker run -d \
|
|
--name twenty-app-dev-test \
|
|
-p 2021:2021 \
|
|
-e NODE_PORT=2021 \
|
|
-e SERVER_URL=http://localhost:2021 \
|
|
-e MARKETPLACE_CATALOG_SYNC_CRON_ENABLED=false \
|
|
-e API_RATE_LIMITING_SHORT_LIMIT=100000 \
|
|
-e API_RATE_LIMITING_LONG_LIMIT=100000 \
|
|
twentycrm/twenty-app-dev:${{ inputs.twenty-version }}
|
|
|
|
echo "Waiting for Twenty test instance to become healthy…"
|
|
TIMEOUT=180
|
|
ELAPSED=0
|
|
until curl -sf http://localhost:2021/healthz > /dev/null 2>&1; do
|
|
if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
|
|
echo "::error::Twenty did not become healthy within ${TIMEOUT}s"
|
|
docker logs twenty-app-dev-test 2>&1 | tail -80
|
|
exit 1
|
|
fi
|
|
sleep 3
|
|
ELAPSED=$((ELAPSED + 3))
|
|
echo " … waited ${ELAPSED}s"
|
|
done
|
|
echo "Twenty test instance is ready at http://localhost:2021 (took ~${ELAPSED}s)"
|