cdeebb1a185e4d53186464fc5ffe576dfd3b6fd9
143 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fea06bdd4b |
v1.5.1 — partners: Discord notification for client briefs + referring-partner attribution (#23344)
**Merge after #23295.** Targets `main`, but must land second: #23295 bumps `1.3.2 → 1.4.0`, and this bumps `1.4.0 → 1.5.1`. Merging this first would leave `main` at 1.5.1 and make #23295's bump conflict and regress the version. `package.json` is the only file the two branches share. App version: **v1.5.1**. ## What this does Posts a Discord notification when a client brief is submitted through the public marketplace form, and records which partner's profile page the brief came from. A visitor can reach the brief form from the marketplace listing page or from a specific partner's profile. Until now that context was lost. This adds a `referredByPartner` relation on Opportunity so the attribution is a queryable CRM fact rather than a line in a chat message. ## How it works `submitClientBrief` resolves the incoming `partnerSlug` to a Partner, sets the relation on create, then posts the embed inline. Inline rather than an `opportunity.created` database trigger, because that event cannot distinguish a brief from a TFT import — both are created by logic functions and both carry `createdBy.source === 'APPLICATION'`. A trigger would need a discriminator like "source is APPLICATION and `tftOpportunityId` is empty", which silently breaks the day a third logic function creates an Opportunity. The cost of going inline is that the Discord call sits in the visitor's request, so it uses a 3s timeout rather than the trigger path's 8s, and every failure is swallowed — a dead webhook can never turn a submitted brief into a failed one. ## Notable decisions - **Slug resolution ignores `validationStage` and `availability`**, unlike the marketplace profile query. If someone submitted a brief from a partner's page, that partner referred it, even if they go unavailable a minute later. Filtering would silently drop real attribution. - **An unresolved slug never fails the brief.** It logs a warning, leaves the relation unset, and still notifies. A brief is a sales lead; losing one over an attribution field the visitor never saw would be a bad trade. - **`referredByPartner` is separate from the existing `partner` field.** One is who sent the lead, the other is who works it. - **The Discord connector moved to `modules/shared/connector/`.** Two domains now need it, and `AGENTS.md` forbids importing logic sideways between domains. `postWebhook` gained `label` and `timeoutMs` parameters; the transport is otherwise unchanged. - Reuses the existing `DISCORD_WEBHOOK_URL` and `PARTNER_APP_FRONTEND_URL` variables — no new configuration to set on prod. ## Permissions `partner.role.ts` locks the new Opportunity field. `configure-partner-rls.ts` treats its skip-list as a closed allowlist of system columns, so an unlocked new field is reported as a discrepancy. Note that Opportunity RLS for partners is `(partnerUser IS me) OR (isListed = true)`, so on a **listed** brief any partner can read `referredByPartner` — i.e. see that a competitor referred it. Called out deliberately; happy to restrict it if that's not wanted. ## Testing 8 unit tests for the embed mapper (partner present/absent, truncation, absent optionals, no email in the payload, inline-row padding) and 4 for the schema. Full suite: 188 passing, lint clean. Verified end to end against a local workspace with a real Discord webhook. All three paths return `ok: true`; the persisted relation was confirmed via GraphQL rather than inferred from the status code: | Submission | `referredByPartner` | |---|---| | valid slug | linked to the partner | | no slug | `null`, embed reads "Marketplace listing" | | unknown slug | `null`, brief still succeeds | ## Follow-up, not in this PR `yarn rls:configure` fails before reaching its field-lock check — its retry path strips `predicateGroups` but the predicates still carry `rowLevelPermissionPredicateGroupId`, so the retry fails identically. Pre-existing and unrelated to this change (`configure-partner-rls.ts` is untouched here), but it means the script cannot currently verify the lock on a fresh workspace. The website side that sends `partnerSlug` is #23351. Until it ships, this is inert: no caller sends the field, and briefs behave exactly as before. Merge this one first — #23351 is the sender, this is the receiver. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23344?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. --> |
||
|
|
e631c986a1 |
v1.4.0 — partners: auto-link partner user on workspaceMember.created (#23295)
**App version:** `1.4.0` (partners app — `packages/twenty-apps/internal/twenty-partners`) ## What Adds partner onboarding auto-linking: when a `workspaceMember` is created (invite signup), a DB-event-triggered logic function resolves the partner by the member's email and stamps `partnerUser` across the partner and its cascade (person, company, links, services, content, applications). ## Key design decision — data-linking only, no role assignment The trigger **does not** assign the Partner role. A logic function runs as an app **agent**, with no user session; `updateWorkspaceMemberRole` is guarded by `UserAuthGuard` + `AuthWorkspaceMemberId` and is unreachable from an agent, so the mutation silently no-ops regardless of permission flags. The dead role code (`ensure-partner-role` service, its role query/mutation, and the role mocks) is removed so the trigger's responsibility is unambiguous: resolve partner by email → link `partnerUser` cascade with retry-on-partial-failure. Role assignment, if wanted, belongs on the invite path (`sendInvitations` accepts a `roleId`), not the trigger. ## Changes - `on-workspace-member-created.logic-function.ts` — DB-event trigger on `workspaceMember.created`; skips internal (`@twenty.com`) and unmatched emails - `resolve-partner-by-email` / `link-partner-user` services + typed `graphql/` operations for the cascade - `normalize-invite-email` util - `partnerUserLinkedAt` field on Partner - Seed: one contact `Person` (with `partnerId` + email) and one `Company` per partner so onboarding is testable via a seeded invite email; drops the `person.city` write removed in SDK 2.25 that broke `yarn seed` ## Verification - Unit: **173/173 pass** (27 files) · `tsc --noEmit` clean · `oxlint` 0 warnings/0 errors - End-to-end: invited + signed in a seeded partner (`lena@act-education.example`) on the workspace subdomain; the trigger linked the member to the **Act Education** partner and the self-service **My Profile** page rendered the linked profile (`POST /s/my-partner-profile → 200`) <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23295?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. --> |
||
|
|
302f46f0ea |
fix: bump brace-expansion to 5.0.8 in app lockfiles (Dependabot) (#23346)
## Summary Bumps **brace-expansion -> 5.0.8** in the three app lockfiles whose copy sits on the 5.x line, clearing **GHSA-mh99-v99m-4gvg** (high, vulnerable `<= 5.0.7`) on those manifests: - `examples/hello-world` (`^5.0.2`) - `examples/postcard` (`^5.0.5`) - `internal/self-hosting` (`^5.0.5`) All three are caret ranges, so a recursive `yarn up -R brace-expansion` lifts them with **no resolution and no `package.json` change**. ## Why the fixtures are not included This advisory declares a single vulnerable range, `<= 5.0.7`, which spans **every** major line - so the `brace-expansion@2.1.2` copies in `seed-dependencies` and `common-layer-dependencies` are flagged as well. But **2.1.2 is the last 2.x release** (1.x likewise ends at 1.1.16), and the only patched version is **5.0.8**. Those consumers declare `^2.0.1` / `^2.0.2`, which caps below 3.0.0, so there is no in-range fix: clearing them would mean forcing a cross-major jump from 2.x to 5.x via a resolution, which is a behavior risk rather than a mechanical lift. Same situation for the root alert ([1765](https://github.com/twentyhq/twenty/security/dependabot/1765)), where `nx` pins `brace-expansion` 5.0.6 exact. ## Verification - brace-expansion resolves to **5.0.8** in all three lockfiles. - `yarn install --immutable` passes in each. - 5.0.8 published 2026-07-23, clears the 3-day npm age gate. |
||
|
|
ed95b8cfde |
fix: bump postcss to 8.5.22 across app lockfiles (Dependabot) (#23340)
## Summary Bumps **postcss -> 8.5.22** in the 13 twenty-apps lockfiles that carry it transitively, clearing **GHSA-r28c-9q8g-f849** (high) on those manifests: path traversal in previous source map auto-loading (`sourceMappingURL`) leading to arbitrary `.map` file disclosure, vulnerable `<= 8.5.17`. Apps covered: document-generator, hello-world, postcard, self-hosting, twenty-partners, call-recorder, people-data-labs, twenty-discord, twenty-exa, twenty-fireflies, twenty-last-contact, twenty-linear, twenty-slack. Every app reaches postcss through a caret range (`^8.5.15`), so a recursive `yarn up -R postcss` lifts it in each project with **no resolution and no `package.json` change** - the diff is 13 `yarn.lock` files and nothing else. Yarn resolves to **8.5.22**, the latest in range (above the 8.5.18 fix floor). ## Not included The **root lockfile** carries the same advisory but its postcss copies are held by exact pins - `next` (8.4.31 in every stable release, including 16.2.11) and `@mintlify/common` (8.5.14, unchanged in its latest) - so no `yarn up` reaches it. That one needs a scoped resolution and is handled separately. ## Verification - postcss resolves to **8.5.22** in all 13 lockfiles; nothing at or below 8.5.17 remains. - `yarn install --immutable` passes in each of the 13 projects. - 8.5.22 published 2026-07-22, clears the 3-day npm age gate. |
||
|
|
155636d7d9 |
fix: bump tar to 7.5.21 across app lockfiles (Dependabot) (#23332)
## Summary Bumps **tar -> 7.5.21** in the 13 twenty-apps lockfiles that carry it transitively, clearing **GHSA-r292-9mhp-454m** (medium) on those manifests: uncontrolled recursion in `mapHas`/`filesFilter` allows an uncatchable stack-overflow DoS via a crafted long-path tar with member selection, vulnerable `<= 7.5.20`. Apps covered: document-generator, hello-world, postcard, self-hosting, twenty-partners, call-recorder, people-data-labs, twenty-discord, twenty-exa, twenty-fireflies, twenty-last-contact, twenty-linear, twenty-slack. Every app reaches tar through a caret range (`^7.5.4`), so a recursive `yarn up -R tar` lifts it in each project with **no resolution and no `package.json` change** - the diff is 13 `yarn.lock` files and nothing else. ## Not included - **Root lockfile**: same advisory, shipped separately in #23330. - **`application-package/constants/seed-dependencies`**: the 14th manifest with this advisory. Its `yarn.lock` is checksum-coupled to `DEFAULT_YARN_LOCK_CHECKSUM`, so it moves in its own PR with the constant regenerated alongside. ## Verification - tar resolves to **7.5.21** in all 13 lockfiles; nothing below remains. - `yarn install --immutable` passes in each of the 13 projects. - 7.5.21 published 2026-07-21, clears the 3-day npm age gate. |
||
|
|
0d876eb714 |
fix: lift axios/tar/brace-expansion/body-parser across app lockfiles (Dependabot) (#23267)
## Summary Sweeps the **twenty-apps lockfiles** for this week's advisory wave: recursive `yarn up` for **axios, tar, brace-expansion, body-parser** in each of the 12 apps with open Dependabot alerts (hello-world, postcard, self-hosting, twenty-partners, call-recorder, people-data-labs, twenty-discord, twenty-exa, twenty-fireflies, twenty-last-contact, twenty-linear, twenty-slack). All moves fit the declared ranges (apps carry these transitively via `twenty-sdk`, whose `axios ^1.16.0` and deep tar/brace chains are carets), so the diff is **lockfile-only** across all 12 manifests - no resolutions, no `package.json` changes. axios -> 1.18.x, tar -> 7.5.20 (critical GHSA-23hp-3jrh-7fpw chain), brace-expansion -> 1.1.16 / 2.1.2 / 5.0.7, body-parser -> 1.20.6 / 2.3.0. The second commit narrows scope to apps only: the twenty-server fixture projects (seed-dependencies, common-layer-dependencies) move to a dedicated PR because seed-dependencies' yarn.lock is checksum-coupled to `DEFAULT_YARN_LOCK_CHECKSUM` in `get-default-application-package-fields.util.ts`; it also drops accidentally committed `.yarn/install-state.gz` artifacts. ## Deliberately not covered - **sharp**: every path is minor-locked at `^0.34.5` (including twenty-sdk latest) - separate PR bumping twenty-sdk's range. - **react-router / react-router-dom**: no fixed release on the 6.x line (fix is the v7 major); tracked separately. ## Verification - Vulnerable-version scan across all 12 lockfiles: no axios <1.18, tar <7.5.19, brace-expansion below 1.1.16/2.1.2/5.0.7, or body-parser below 1.20.6/2.3.0 remains. - `yarn install --immutable` passes in each app. - All fix versions clear the 3-day npm age gate. |
||
|
|
940d150775 |
chore(self-hosting): upgrade to latest twenty CLI tooling (#23279)
## What Upgrades the internal `self-hosting` app to the latest Twenty CLI tooling, bringing it in line with the other actively-maintained apps in the monorepo. - `twenty-sdk`: `2.19.0-alpha.1` → `2.23.0-alpha.2` (dependency + devDependency) - `twenty-client-sdk`: `2.19.0-alpha.1` → `2.23.0-alpha.2` (dependency + devDependency) - `engines.twenty`: `>=2.19.0` → `>=2.23.0` - Regenerated `yarn.lock` to match. The `twenty` CLI ships inside `twenty-sdk`, so this pulls the app onto the same CLI version every other recently-updated app (call-recorder, people-data-labs, twenty-partners, real-estate, last-contact, postcard) already uses. ## Verification - `yarn typecheck` passes - `yarn lint` passes (0 warnings, 0 errors) - `yarn test:unit` passes (3/3) Integration tests (`yarn test`) require a running Twenty server and were not run in this environment. ## Notes Scope is limited to the CLI/SDK tooling. Framework deps (React 18) were left untouched since they are not tied to the CLI version and vary across apps. --- _Generated by [Claude Code](https://claude.ai/code/session_01HUPkxerLhethaP9Lw6qDyd)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23279?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. --> |
||
|
|
d1c6b8ee72 |
Show relation record labels instead of UUIDs in dashboard charts (#23163)
https://github.com/user-attachments/assets/d012a013-2c90-49a1-a27e-b8e4b684a84f Charts grouped by a relation without a sub-field rendered raw FK UUIDs on axis ticks, legends and tooltips. The server now batch-resolves the grouped record ids to their label identifier through a permission-scoped query and formats every bucket with the record's display name. Unresolvable records (deleted or not readable) render as Unknown and their ids are stripped from the response payload. Same-named records get an ordinal suffix so their buckets don't merge. Covers bar, line and pie, plain and morph relations. ```mermaid flowchart TD A["Dashboard widget load"] --> B["Chart data service<br/>(bar / line / pie)"] B --> C["executeGroupByQuery:<br/>group by relation FK id,<br/>ORDER BY target label identifier,<br/>scoped to source object permissions"] C --> D["filterOutEmptyChartBuckets"] D --> E{"Bare relation axis?<br/>(no sub-field)"} subgraph RL["ChartRelationLabelService.resolveRelationLabels"] direction TB G1["Collect distinct record ids<br/>per target object"] --> G2["Batch SELECT label identifier columns,<br/>scoped to TARGET object permissions"] G2 --> G3["buildRawLabelByRecordId:<br/>display name per record"] G3 --> G4["buildUniqueRelationLabels:<br/>suffix duplicates, Unknown for unresolved"] end E -- No --> H["formatDimensionValue per bucket"] E -- Yes --> G1 G4 --> H H --> I["Strip unresolved ids from<br/>formattedToRawLookup"] I --> J["Chart DTO to frontend"] ``` The chart settings sub-field dropdown gains a Record option to group by the related record itself, and now only offers sub-fields the backend accepts (system fields like a workspace member's updatedBy were selectable but rejected at query time). Chart-data errors are now logged server-side. Also fixes two latent bugs on this path: sorting a bare-relation chart by field threw `Cannot orderBy unknown field: agentId`, and the pie chart truncated slices before sorting. The AI dashboard tool guidance and the seeded dashboards no longer force the sub-field workaround. The group-by query orders buckets by the related record's label identifier at the database level (the engine now accepts ordering by a target field when grouping by its id), so with more than 100 distinct related records the surviving buckets match the label order. |
||
|
|
18fb0946e6 |
v1.4.0 — Raise partner bar: Twenty experience fields + triage (#23224)
## Summary **Version:** `twenty-partners` **v1.4.0** (minor — new Partner fields + apply contract) - Add Partner fields `twentyExperience`, `twentyExperienceNotes`, `twentyExperienceProofLink` and persist them from `submit-partner-application` (≥200-char narrative at API boundary) - Surface Twenty experience on applications / validated / per-stage triage views and the Partner record side panel (drop empty Introduction from that panel) - Add pure Tally CSV match/map helpers (ops import script stays outside the repo) for backfilling existing partners by `partnerId` **Companion PR (website):** #23223 — Experience step on apply + thank-you without Cal. ## Test plan - [ ] `yarn twenty apply -r <remote>` on a workspace — Partner gains the three experience fields - [ ] Website apply (with #23223) persists milestones / notes / proof link on create and email-linked update - [ ] Applications + Validated views show experience columns; record side panel lists experience fields - [ ] `yarn lint` clean; `yarn test:unit` covers schema + map-tally helpers - [ ] After Tally campaign: dry-run then apply CSV import via local ops script under `~/twenty/docs/superpowers-specs/raise-bar-import/` |
||
|
|
9d8ce7c325 |
v1.3.2 — Modularize partners app into vertical-slice modules/ (#23168)
**Version:** `twenty-partners@1.3.2` (patch — internal refactor, no
visible behavior change)
## What & why
Reorganizes the `twenty-partners` SDK app from a flat, type-first layout
(`src/{objects,fields,views,logic-functions,front-components,…}`) into a
**vertical-slice** layout under `src/modules/<domain>/<feature>/`. Files
that
change together now live together; each SDK entrypoint is a thin
discoverable
shim over a service + graphql-ops + mapper/connector layer.
This is a pure structural refactor — **no object, field, view, enum,
logic
function, trigger, role, or application variable changed.**
## Final layout
```
src/modules/
shared/ http · services · graphql · utils · front-components · navigation-menu-items (cross-domain nav folders)
opportunity/ fields·view-fields·views·navigation-menu-items·page-layouts·constants + intake/ + matching/
partner/ objects·fields·constants·utils + directory/ · self-service/ · marketplace/ · application-intake/ (Discord connector/)
application/ objects·fields·views·navigation-menu-items·page-layouts + services · graphql
```
Every `defineLogicFunction` entrypoint is now a thin
`*.logic-function.ts`
(all < 40 lines) at its domain/feature root, delegating to a
`*.service.ts`;
graphql operations live in `graphql/{queries,mutations}/`, pure
transforms in
`mappers/`, outbound APIs (the Discord webhook) in `connector/`, pure
helpers
in `utils/`.
## Safety — the load-bearing invariant
The server diffs app primitives by `universalIdentifier`, so a
changed/dropped
UUID would drop-and-recreate the object on prod (data loss). This branch
holds
that line:
- **887 `universalIdentifier`s byte-identical** to the branch base
(every
relocation is a `git mv`; every extracted entrypoint keeps its original
UUID/name/trigger verbatim). Re-verified byte-identical across the
rebase.
- `yarn twenty dev --once` against a live workspace = **"No changes.
Twenty
metadata matches your manifest."**, confirmed idempotent on a second run
—
the whole refactor is a metadata no-op (zero create/delete/identity
change).
- Every extracted graphql op was verified **byte-identical** to its
original
(args, `first:` caps, pagination, selection sets), and the
partner-application
Discord embed's deliberate PII omission (no email / hourly rate) is
preserved.
## Rebased onto latest `main`
This branch is rebased onto `main` (`d20e5378fd`) and now carries main's
`twenty-sdk` / `twenty-client-sdk` **2.23.0-alpha.2** bump.
Note for reviewers: main had independently bumped this package to
`1.3.1`, so
the original `1.3.0 → 1.3.1` commit here was redundant and git dropped
it during
the rebase (`patch contents already upstream`) — with **no textual
conflict**,
since both sides wrote the same version string. The bump is therefore
now
**`1.3.2`**. The rebase touched only `package.json` and `yarn.lock`;
**every line
of refactored source is byte-identical** to the pre-rebase tree.
## Verification
All run on the rebased tree, against SDK `2.23.0-alpha.2` and a live
Twenty
server `v2.23.2`:
| Check | Result |
|---|---|
| `universalIdentifier` set | 887, byte-identical |
| `yarn twenty dev --once` | "No changes" (idempotent on re-run) |
| Typecheck | pass |
| `yarn lint` | 0 warnings, 0 errors (287 files) |
| `yarn test:unit` | 158/158 (23 files) |
| `yarn test:integration` | 45/45 (13 files) |
## Also in this PR
- **Architecture convention doc** — `AGENTS.md` (+ a one-line
`CLAUDE.md` pointer)
at the package root documents the vertical-slice conventions this
refactor
establishes: the layout, the dependency rule (`logic-function → service
→
graphql/connector`), file naming, connector = outbound-only (inbound
webhooks
are logic-functions), and the UUID invariant. It ships here so the doc
and the
structure it describes land together.
- **`modules/shared/`** dedup: the secret-guarded intake envelope, the
find-or-create-company/person helpers + their graphql ops, `collectAll`
pagination, `http-url`/`strip-markdown`/`is-non-empty-string` utils.
- **Vitest configs collapsed** into one `vitest.config.ts` with `unit` +
`integration` projects (`yarn test:unit` / `yarn test:integration`).
- Cross-domain nav folders (`pipeline-folder`,
`partner-workspace-folder`)
hoisted to `modules/shared/navigation-menu-items/`.
## Deferred (non-blocking, tracked follow-ups)
- Add direct unit tests for the shared `collectAll` / `isNonEmptyString`
utils
(currently covered indirectly).
- Move `submit-client-brief`'s zod schema out of its mapper file into
its own
schema file (mirroring the partner side).
- Route `stamp-partner-user-on-child` through the shared self-service
mutation ops.
- `find-partner-by-member.ts` is duplicated identically in the
`application` and
`self-service` domains; a candidate to hoist into `modules/shared/`.
|
||
|
|
8d943e1f68 |
Bump to alpha 2 all published apps (#23165)
Related https://github.com/twentyhq/twenty/pull/23155 <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23165?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. --> |
||
|
|
623aac5a56 |
Show agent names on real estate dashboard charts (#23146)
Follow-up to the real estate demo app: the Agency Overview dashboard's "Listings by agent" and "Showings by agent" bar charts grouped by the agent relation, which rendered the agent's UUID on the axis. Adding `primaryAxisGroupBySubFieldName: 'name.firstName'` groups by the agent's first name instead, so the charts show agent names (Emma, Lucas, Chloe, Louis). <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23146?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. --> |
||
|
|
9a374c4b6d |
Add real estate demo app (#23111)
## What A new app under `packages/twenty-apps/internal/real-estate` that seeds a real-estate demo workspace: buyers, sellers, agents, property listings, showings, and an opportunity pipeline, with role-based access. ## Data model - **Property** (custom object): address, price (currency), status (coming soon / active / under offer / sold), type, beds/baths/surface, photos, `listingAgent` and `sellerContact` relations to Person. - **Showing** (custom object): scheduledAt, status, feedback, interest rating, and `property` / `buyer` / `agent` / `opportunity` relations. - **Person** (standard, extended): `personType` (Buyer / Seller / Agent), budget min/max, pre-approved, desired area. - **Opportunity** (standard, extended): `buyerStage` pipeline (completing profile → showing → offer made → closing → won → lost), and `buyer` / `seller` / `property` / `showings` relations. ## Views - **Buyer Pipeline** — kanban on Opportunity grouped by buyer stage, one card per buyer, scoped to real-estate deals. - **Available (by price)** — properties sorted by price desc, excluding sold. - **Agents** / **Buyers** — filtered Person views. - Record-page layouts for Opportunity and Showing so the relations render on the detail pages. ## Roles - **Broker** — full access (default). - **Agent** — Property / Showing / Person / Note / Task, no Opportunity access. - **Seller** — read-only on their listing and its showings. ## Seeding A synchronous post-install logic function seeds 4 agents, 12 sellers, 12 buyers, 30 properties across 5 cities, 24 showings, and 12 opportunities (one per buyer), all wired through the relations. --------- Co-authored-by: prastoin <paul@twenty.com> |
||
|
|
2ef7b7824e |
Upgrade call-recorder, people-data-labs, last-contact and partners apps to twenty-sdk 2.23.0-alpha.1 (#23098)
## What Upgrades the two breaking-change-prone apps to `twenty-sdk` / `twenty-client-sdk` `2.23.0-alpha.1`, and adds the server-side hook that lets the 2.23 upgrade install them: - **people-data-labs** - **partners** Follows up on #22882 (System side effect relations), which re-derived the system relation field universal identifiers name-free and shipped `getSystemRelationFieldUniversalIdentifier` in the SDK. ## How - **people-data-labs**: bump the SDK to `2.23.0-alpha.1`. The enriched views temporarily hardcoded the new system relation identifiers with a TODO because the SDK still embedded the old values; now that the name-free identifiers ship in `2.23`, derive them from `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.{company,person}.fields.{noteTargets,taskTargets,attachments,timelineActivities}.universalIdentifier` (identical to the previously pinned values, verified). Engine already pinned `twenty >=2.23.0`; app stays `1.0.7` (manifest unchanged). - **partners**: bump the SDK to `2.23.0-alpha.1`. The partner role references `opportunity.fields.{taskTargets,noteTargets,attachments,timelineActivities}` universal identifiers, which the SDK now resolves to the `2.23` name-free values. Pin `engines.twenty >=2.23.0` and bump the app to `1.3.1`. - **server**: add an opt-in `skipWorkspaceCompatibilityCheck` to the install/upgrade path. The `upgrade-people-data-labs-application` 2.23 command runs mid-upgrade, before the workspace is marked as having completed 2.23, so the workspace-compatibility check would otherwise reject installing `1.0.7` (`engines >=2.23.0`). The server is already on 2.23, so the command passes the flag to install `1.0.7` and close the desync window. Version-progression (downgrade/same-version) checks still run. - **call-recorder** and **last-contact** are intentionally left unchanged (reverted): they don't define custom objects and don't reference the system relation identifiers, so they aren't breaking-change-prone and need no SDK bump. ## Breaking change constraints - **people-data-labs** and **partners** reference system relation identifiers that only exist on a `2.23` server, so both pin `engines.twenty >=2.23.0`. Their `dockerhub-latest` integration leg is red by design until a >=2.23 server image is published (same accepted state as #22882); the `local` leg is green. ## Validation - Regenerated the app lockfiles against the published `2.23.0-alpha.1`. - `people-data-labs` typechecks cleanly against the real `2.23` SDK types. - CI: people-data-labs and partners green on `local`, red on `dockerhub-latest` by design; server/SDK/all other checks green. - Rebased onto latest `main`. |
||
|
|
e6c6cccafa |
v1.3.0 — Partner workspace self-service (glowup app) (#22929)
## Glowup — app · v1.3.0 (Release ② of the brief + glowup rollout) Partner **workspace self-service**: partners manage their own profile, links, services, and case studies from inside the CRM (new objects + record-page views + a "My Profile" self-service front-component). Evolved superset of the closed #22470 (v1.3.0). App-only — **0 website files**. Version **1.3.0** (prod is currently 1.2.10). SDK **2.19.0**. Supersedes **#22470** (closed). ### Verified locally Provisioned a throwaway workspace, synced the schema, seeded, and exercised the full surface end-to-end: marketplace + public profiles render live; **partner self-service pages** (My Profile / My Case Studies / links / services) load and save when acting as a partner user; both intake forms (partner application + client brief) submit successfully. `oxlint` 0/0, typecheck clean. ### Notes - Committed `APPLICATION_UNIVERSAL_IDENTIFIER` is the **canonical** prod id `e662fc1f-02c1-41ff-b8ba-c95a447b3965` (local bundle rewrites it to a throwaway that stays uncommitted). - New views reference app-owned fields only — no hardcoded system-field ids. ### Remaining before merge - CI lint / typecheck / tests (green locally). - Refresh the partners-doc (new objects/views change the app surface). --- ## 🚦 Release order — do not break ``` ① BRIEF WEB — #22291 ✅ MERGED (website deploy pending prod CLIENT_BRIEF_* env vars) │ ▼ ② GLOWUP APP — THIS PR (rk-partner-profile-page v1.3.0 → main) ⟵ replaces #22470 merge → DEPLOY TO PROD (verify canonical id first, yarn twenty deploy && install -r partner-twenty-com) → set new app variables on prod → refresh partners-doc │ ⟵⟵ GATE for ③ ⟵⟵ ▼ ③ GLOWUP WEB — rk-glowup-web-stacked (reopen ONE PR, base main; was #22471 / #22402) ONLY after ② is LIVE on prod (the site reads the new links / services / case-study objects) ``` - ② gates only ③. After ② deploys, reconcile **#22637** (partners-traffic-web) with ③ — both touch `partners-marketplace/*`. |
||
|
|
bc1ccf526f |
chore(twenty-partners): upgrade to twenty-sdk 2.21 (#22869)
## What Upgrades the `twenty-partners` internal app to consume twenty-sdk 2.21. - Bump `twenty-sdk` and `twenty-client-sdk` from `2.19.0-alpha.1` to `2.21.0` (`package.json` + `yarn.lock`). - Replace the removed `generateDefaultFieldUniversalIdentifier` helper with `getFieldUniversalIdentifier` in `partner-applications.view.ts`, renaming the `fieldName` argument to `name`. ## Why `generateDefaultFieldUniversalIdentifier` was removed from the SDK and replaced by `getFieldUniversalIdentifier`. Both compute the same deterministic uuid-v5 (`fieldMetadata:objectUID:name` under the app universal identifier), so the resolved `createdAt` field identifier is unchanged; this is the only code change required to build against 2.21. ## Verified - Install resolves to 2.21.0; runtime check confirms `getFieldUniversalIdentifier` is exported and `generateDefaultFieldUniversalIdentifier` is gone. - Type check: no real errors. - `oxlint`: 0 warnings, 0 errors. --- _Generated by [Claude Code](https://claude.ai/code/session_01RUQ2yyfZcyKWqG57HbUYxx)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22869?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. --> |
||
|
|
ace16add6c |
fix(twenty-partners): align createdAt view field with 2.19 deterministic ids and bump to 1.2.10 (#22782)
## What - Bump `twenty-partners` from `1.2.0` to `1.2.10`. - Fix the red integration tests by pointing the "Partner Applications" view `createdAt` column at the real field metadata id. ## Why the integration tests were red The `twenty-partners` CI job spins up `twentycrm/twenty-app-dev:latest` and runs the integration suite. The suite's global setup does a dev sync of the app, which failed: ``` Dev sync failed: viewField: INVALID_VIEW_DATA: Field metadata not found (universalIdentifier: 835c9a7e-72ec-46c5-8d90-39a02998f561) ``` The `partner-applications.view.ts` `createdAt` column referenced `PARTNER_CREATED_AT_FIELD_UNIVERSAL_IDENTIFIER = 421cbcea-...`, an invented id. `createdAt` is a reserved system field auto-created on the custom `partner` object, and since 2.19 its universal identifier is derived deterministically by the server from the application id, the object id and the field name. The invented id matched nothing, so the sync rejected the dangling view field and the app never registered, failing every integration test. ## Fix Set `PARTNER_CREATED_AT_FIELD_UNIVERSAL_IDENTIFIER` to the deterministically derived value `746e2944-28d0-545e-9832-a46516e1d9a0` (application id `e662fc1f-...` + partner object id `39101b39-...` + field name `createdAt`). This matches the same derivation the server uses for standard object system fields, verified against the `twenty-shared` opportunity `createdAt` snapshot. |
||
|
|
fe442fe5fe |
chore(apps): bump sdk to 2.19.0-alpha.1 and require twenty server >=2.19.0 (#22601)
## Summary
- Bumps `twenty-sdk` / `twenty-client-sdk` to the exact `2.19.0-alpha.1`
prerelease for the apps under `packages/twenty-apps` that actually
target a mutated standard identifier, and refreshes their lockfiles.
- Declares `"engines": { "twenty": ">=2.19.0" }` in those apps so
pre-2.19 servers refuse to install or upgrade to the rebuilt packages.
Only apps that reference a standard object's **system-field** universal
identifier, define a **relation into** a standard object, or call the
field-UID derivation helper need 2.19 (the identifiers those touch
changed from hardcoded UUIDs to deterministic hashes). Apps that only
define their own custom objects, or add plain scalar fields to a
standard object via its stable object-level id, were left on their prior
SDK pins. Currently bumped: `postcard`, `self-hosting`,
`twenty-partners`, `call-recorder`, `people-data-labs`,
`twenty-last-contact`.
## Context
Follow-up to #22565 (deterministic system field universal identifiers)
and #22599 (SDK prerelease bump).
Packages built with SDK ≤ 2.18 carry legacy system field identifiers and
are rejected by servers running `main`. Rebuilding with the 2.19 SDK
fixes that — but a rebuilt package must not be *upgraded into* by a 2.18
server, since 2.18 has no deterministic-identifier validation and would
diff the changed system field identifiers as a destructive delete +
create (the 2.19 backfill has not run there yet).
The `engines.twenty` constraint closes that gap: `doInstallApplication`
validates it via `validateServerCompatibility` before any mutation, and
this check has shipped since ~2.10, so every 2.18 server enforces it.
Resulting matrix:
- 2.18 fresh install of a rebuilt app: works, converges as a no-op once
the 2.19 backfill runs
- 2.18 upgrade of an existing install: rejected with
`SERVER_VERSION_INCOMPATIBLE` before any mutation
- 2.19 (post-backfill) install/upgrade: syncs cleanly
## Expected CI failures
**The `CI Twenty Apps` integration-test jobs are expected to fail on
this PR** (e.g. `people-data-labs`, `twenty-partners`). This is a
server-version mismatch, not an app bug — lint, typecheck and unit tests
all pass:
- The integration step spawns a real Twenty server from Docker Hub
`twentycrm/twenty-app-dev:latest` and runs `twenty dev` to sync each
app's metadata into it.
- `latest` currently resolves to **v2.18.5** — no `2.19` image is
published to Docker Hub yet.
- These apps now reference 2.19's **deterministic system-field universal
identifiers** (e.g. `company.createdBy`, `opportunity.createdAt`). A
2.18 server still carries the legacy identifiers, so the sync rejects
every 2.19-derived reference with `INVALID_VIEW_DATA` /
`FIELD_METADATA_NOT_FOUND` ("Field metadata not found").
- The failure surfaces as low-level field errors rather than a clean
`SERVER_VERSION_INCOMPATIBLE` because the `engines.twenty` gate
(`validateServerCompatibility`) only runs on the `app:install` / publish
paths — **not** on the `twenty dev` dev-sync path the integration tests
use.
These jobs will go green automatically once `twenty-app-dev:2.19` is
published to Docker Hub (or once CI pins the spawn action's
`twenty-version` to a 2.19 tag).
## Intentionally not included
- App `version` fields are untouched; they'll be bumped at publish time.
## Test plan
- [x] Refresh each bumped app's `yarn.lock` (`2.19.0-alpha.1` is now on
npm)
- [ ] Rebuild one app manifest and verify default field identifiers
match `getFieldUniversalIdentifier`
- [ ] Verify a 2.18 server rejects an upgrade to a rebuilt package with
`SERVER_VERSION_INCOMPATIBLE`
- [ ] Re-run `CI Twenty Apps` integration jobs once a
`twenty-app-dev:2.19` image is available
Made with [Cursor](https://cursor.com)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22601?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-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
|
||
|
|
6c40c7b91a |
Deterministic system field universal identifier (#22565)
# Introduction Close twentyhq/core-team-issues#2641 Auto-provisioned field metadata used to get its `universalIdentifier` from three unrelated sources: random `v4()` on the server when creating custom objects, hardcoded values in `STANDARD_OBJECTS`, and an ad-hoc `v5` derivation in the SDK manifest build. This PR unifies all of them behind the shared `getFieldUniversalIdentifier` derivation: ``` universalIdentifier = f(applicationUniversalIdentifier, objectUniversalIdentifier, fieldName) ``` ## Ownership model The rollout is built on an explicit split of who owns a field's universal identifier: - **The 8 system fields** (`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`) are **server-owned**. Their universal identifiers are always the deterministic derivation, on **every** application (standard, workspace-custom, installed). Clients cannot provide custom values: a temporary check in `validateObjectMetadataSystemFieldsIntegrity` rejects any non-derived system field identifier at migration build time. This check stands in until system fields are generated exclusively server side by the metadata side-effect engine and stripped from client inputs — at which point it becomes structurally impossible to send one. - **`name` is a default field, not a system field**: it is auto-provisioned when absent (server side for custom objects, SDK side for application objects) but authors can define their own. It is only derived where it is guaranteed to be auto-provisioned. In particular, standard objects keep their **historical hardcoded** `name` identifiers: the standard app authors its `name` fields like any installed app would, and moving those identifiers would break every installed application referencing them (e.g. views on `opportunity.name`). - **User-created and author-provided fields** keep random / explicit identifiers, untouched. ## Server - `validateObjectMetadataSystemFieldsIntegrity` now validates, on top of the existing type/`isSystem` checks, that each system field's `universalIdentifier` equals the deterministic derivation. Runs for every object creation going through the migration orchestrator: app sync, custom object creation, standard provisioning - `build-default-flat-field-metadatas-for-custom-object.util.ts` derives the system field identifiers (and the auto-provisioned `name`) with `getFieldUniversalIdentifier` instead of `v4()` - `build-default-relation-flat-field-metadatas-for-custom-object.util.ts` derives both the forward and the reverse default relation field identifiers deterministically - `generateMorphOrRelationFlatFieldMetadataPair` accepts optional `sourceFieldUniversalIdentifier` / `targetFieldUniversalIdentifier` so callers can inject deterministic values; user-created relations still default to `v4()` ## twenty-shared - `STANDARD_OBJECTS` system field identifiers (the 8) are now computed at module load via `buildStandardObjectSystemFields`; `name` and every other identifier keep their hardcoded values - New snapshot test pinning **every** universal identifier of `STANDARD_OBJECTS`: any identifier change now requires an explicit snapshot update and should ship with a coordinated backfill ## SDK (breaking, pre-GA) - `generateDefaultFieldUniversalIdentifier` delegates to `getFieldUniversalIdentifier` and now requires `applicationUniversalIdentifier` - Reverse default relation field identifiers are derived from the field's real coordinates (standard object UID + actual field name, e.g. `targetRocket` on `attachment`) instead of the legacy custom-object UID + synthetic `${fieldName}Inverse` hash input. Field *names* are unchanged - The manifest build threads the application universal identifier through default field injection (two-pass over object configs) - `twenty dev:add` now resolves the application universal identifier upfront and refuses to scaffold anything until `defineApplication` declares one — no more `fill-later` placeholder for the app UID in generated files ## Upgrade A 2.19 **workspace command** backfills existing `fieldMetadata.universalIdentifier` rows to the deterministic derivation. Coverage follows the ownership model: - **The 8 system fields**: taken over for **every application**, whatever value they currently hold. This is both safe and required now that sync rejects non-derived values — leaving a row unconverged would make its application unsyncable - **`name`**: workspace-custom app → always taken over (server-generated, no author to clobber); installed applications → only rows still carrying the legacy SDK derivation are recomputed, author-provided identifiers are never touched; standard app → never touched (hardcoded in `STANDARD_OBJECTS`) - **Default relation fields**: workspace-custom app → forward fields on custom objects and reverse fields on the standard relation objects; installed applications → legacy-derivation probe only All identifiers of a workspace are updated inside a single transaction, then the command flushes the field-metadata-related workspace caches and bumps the metadata version. Stored `applicationRegistration.manifest` snapshots are intentionally **not** rewritten: installs and upgrades always sync from the `manifest.json` inside the resolved package (npm/tarball), the stored column is only used for display/marketplace purposes. ## Breaking behavior for old packages (fail closed) Packages built with an older SDK carry legacy system field identifiers in their tarball `manifest.json`. Installing or upgrading such a package now fails with an explicit `INVALID_SYSTEM_FIELD` validation error ("universal identifier is not deterministic") instead of silently mismatching against the backfilled rows and triggering a destructive delete+create. The remediation is to rebuild the package with the new SDK; the backfill has already converged the installed rows, so the rebuilt manifest syncs cleanly. ## Test plan - [x] `twenty-sdk` unit tests (526 tests) and typecheck - [x] `twenty-shared` unit tests (1635 tests) including the `STANDARD_OBJECTS` snapshot; `name` identifiers verified byte-for-byte identical to `main` - [x] Lint and typecheck clean on all touched packages - [x] Integration: create a custom object and verify system + default relation field identifiers match the deterministic derivation (`create-one-object-metadata-deterministic-field-universal-identifiers`, 13 assertions passing) - [x] Integration: `failing-sync-application-object-system-fields` extended with a non-derived system field identifier case; all identifiers in the spec pinned deterministically so snapshots embedding expected/actual values are stable across runs (verified with a double run) - [x] Integration: all application sync suites pass with the derived system field identifiers now required by the `buildDefaultObjectManifest` test helper (9 suites, 20 tests) - [x] Full test-database reset: standard app provisioning and seeded workspaces pass the new validation - [x] SDK manifest build verified on the postcard example app: all auto-generated default field identifiers match the derivation - [ ] Run `upgrade:2-19:backfill-deterministic-field-universal-identifiers` (dry-run then real) on a seeded workspace and verify identifier convergence with a rebuilt app manifest |
||
|
|
74b3a4216e |
chore(apps): remove the twenty-for-twenty app (consolidated into twenty-eng) (#22549)
Removes the internal `twenty-for-twenty` app from this public repo. It has been consolidated into the private `twenty-eng` monorepo, which now also owns the Resend module (moved there in a companion PR). - Removes `packages/twenty-apps/internal/twenty-for-twenty/**` (177 files). - No build wiring referenced it (no nx project, not in `nx.json`/root workspaces); the only mention elsewhere is a naming-convention comment in `twenty-linear`. ## ⚠️ Sequencing - This removes **source only** — it does **not** uninstall the app currently deployed on the workspace. Merge only **after** the twenty-eng app has taken over the Resend objects there, so the live integration isn't left orphaned. - Supersedes the migration-plan doc PR (#22546), which added a doc into this now-removed directory; that doc now lives in the twenty-eng app. #22546 can be closed. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22549?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. --> |
||
|
|
6b2405c2e9 |
feat(twenty-for-twenty): bump to SDK 2.18 with local UI components (#22542)
## What & why Bumps the **Twenty for Twenty** internal app to the current SDK line and serves it on the matching `twenty-app-dev` image. - `twenty-sdk` / `twenty-client-sdk`: `^2.14.0` → **`^2.18.0`** (npm latest) - Runs against `twentycrm/twenty-app-dev:v2.18.5` (same 2.18 line) - App version `0.1.0` → **`0.2.0`** ### The `twenty-sdk/ui` break twenty-sdk 2.18 **removed the `twenty-sdk/ui` subpath** (#22326, "Remove twenty-ui reexport from the SDK"). The intended replacement — `twenty-ui@1.0.0-alpha.1` subpaths — requires **React 19 + a monaco-editor peer** that this React-18 app can't adopt, so the app no longer builds against 2.18 as-is. Instead of migrating to twenty-ui, this replaces the four `twenty-sdk/ui` consumers with **self-contained local components** under `src/ui/`, imported via a new `@ui` alias: - `Callout`, `H2Title`, `Status` - Tabler-style inline-SVG icons (`IconAlertCircle`, `IconInfoCircle`, `IconMail`, `IconRefresh`, `IconHelp`) - a `ThemeColor` type They mirror the twenty-ui components 1:1 using the `--t-*` theme CSS variables the front-component host injects (the same inline-style pattern the app already used for theme tokens) — no new runtime deps, no React 19 requirement. ## Test plan - `twenty dev:build` ✓, `yarn typecheck` ✓, `yarn lint` ✓ - Synced into a local `twenty-app-dev:v2.18.5` container (`twenty dev --once`) — objects/fields/views/app install applied cleanly - Verified rendering live: Sync Status page (`H2Title` headings + `Status` "Not synced" pills) and all three `Callout` variants (error/info/neutral) with correct colors + icons <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22542?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. --> |
||
|
|
e0ebe0e029 |
v1.2.0 — Client brief intake (marketplace B2) (#22290)
## Summary **Version:** 1.2.0 (`packages/twenty-apps/internal/twenty-partners/package.json`) Adds marketplace B2 client brief intake to the partners app: - `POST /s/client-briefs` logic function — validates payload, creates an unlisted Opportunity (`isListed = false`) with `— client brief` name suffix - **Client briefs** ops view for review before listing - Unit + integration tests Merge this **before** the website PR (`rk-client-brief-web`). ## Test plan - [ ] `yarn twenty dev --once` syncs schema - [ ] `yarn test` passes in `twenty-partners` - [ ] Smoke: `POST /s/client-briefs` with `x-application-secret` creates Opportunity with `isListed = false` - [ ] Client briefs view visible in nav <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22290?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. --> |
||
|
|
0dc6272da5 |
Remove twenty-ui reexport from the SDK and use twenty-ui directly (#22326)
## What & why Removes the `twenty-sdk/ui` reexport. Apps now use Twenty UI by installing [`twenty-ui@1.0.0-alpha.1`](https://www.npmjs.com/package/twenty-ui/v/1.0.0-alpha.1) from npm and importing its subpaths directly. The reexport re-exported types that didn't resolve, forcing typecheck workarounds. ## Changes - **twenty-sdk**: delete `src/ui/index.ts`, drop the `./ui` export, remove it from the browser vite build, and rewire the CLI manifest-mock to `twenty-ui` (`.css` falls through to the empty-CSS loader). `twenty-ui` stays a devDependency for the CLI fixture tests. - **Renderer + create-twenty-app template**: import from `twenty-ui` subpaths; the template pins `twenty-ui@1.0.0-alpha.1`. - **Docs**: new "Using Twenty UI components" section (install + subpath imports + `useTheme()` for theme tokens), codex references, and the cross-doc-contract validator. The `twenty-for-twenty` / `twenty-slack` example apps are intentionally left on `twenty-sdk/ui`: they consume the published SDK (which still ships `./ui`), and `twenty-ui@1.0.0-alpha.1` requires react 19 + a `monaco-editor` peer the react-18 apps can't satisfy. They migrate once the SDK is republished. |
||
|
|
2af5370749 |
v1.1.16 — Restore Partner slug in side panel and Notes tab (#22165)
## Summary **Package version:** `1.1.16` - Adds **slug** to the Partner record-page `FIELDS_WIDGET` view so it appears in the side panel for admins and partners (partners remain update-locked on slug via `partner.role.ts`). - Restores the **Notes** tab on the custom Partner `RECORD_PAGE` layout — the marketplace v2 layout replaced the platform default but only included Home + Timeline. ## Test plan - [ ] `yarn lint` in `packages/twenty-apps/internal/twenty-partners` — 0 errors - [ ] `yarn twenty dev --once` on a local partners workspace — sync succeeds - [ ] Admin: open a Partner record full page → slug visible under Name in side panel; **Notes** tab present and can create a linked note - [ ] Partner role (My Profile): slug visible, not editable; Notes tab works - [ ] After merge: `deploy` + `install` on prod partners workspace <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22165?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. --> |
||
|
|
811fecc119 |
chore(apps): move call-recorder to public, remove stale meeting-bot duplicate (#22132)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22132?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. --> |
||
|
|
c71663946e |
chore(apps): move public apps to packages/twenty-apps/public and generalize CI workflow (#22096)
## What Introduces a `packages/twenty-apps/public/` folder and moves the publicly publishable apps into it, then generalizes the apps CI workflow to cover both folders. ### Moves The following apps were moved from `packages/twenty-apps/internal/` to `packages/twenty-apps/public/` (via `git mv`, history preserved): - `people-data-labs` - `twenty-discord` - `twenty-exa` - `twenty-fireflies` - `twenty-last-contact` - `twenty-linear` - `twenty-meeting-bot` - `twenty-slack` These remain in `internal/`: `self-hosting`, `twenty-for-twenty`, `twenty-partners`. ### Workflow - Renamed `.github/workflows/ci-internal-apps.yaml` → `.github/workflows/ci-twenty-apps.yaml`. - The discover job now scans **both** `packages/twenty-apps/internal` and `packages/twenty-apps/public`: - the "no nested `.github`" guard checks both folders, - `changed-files` watches both globs, - the matrix builder iterates over both roots (guarded with `existsSync` so a missing folder is a no-op). - Each matrix entry still carries its own `path`, so the `ci` job works unchanged regardless of which folder an app lives in. ## Notes - The apps are standalone packages (own `yarn.lock`, not part of the root Nx workspaces), so no root `package.json` / `nx.json` / `tsconfig` changes were needed. - The companion publish workflow lives in `twentyhq/twenty-infra` (`publish-internal-apps.yaml` → `publish-public-apps.yaml`) and is updated in a paired PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01Fmu3DWf1yTTkVW49eSkXwh)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22096?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. --> |
||
|
|
53bfc6ab1c |
fix: update skill to fit requirements (#22097)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22097?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. --> |
||
|
|
0bccbb5035 |
rename twenty meeting bot to call recorder (#22093)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22093?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. --> --------- Co-authored-by: martmull <martmull@hotmail.fr> |
||
|
|
411aee8b96 |
update readme file for meeting bot (#22069)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22069?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. --> |
||
|
|
73e9374ef8 |
[BREAKING CHANGE] harden call recording failure handling (#22062)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22062?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. --> |
||
|
|
eb53dee3be |
fix(twenty-partners): opportunity stage constants + Partners per Stage table (v1.1.9) (#22052)
## Summary Two related cleanup items for the partners workspace: ### 1. Opportunity `stage` field — shared constant + reference catalog - Adds `src/constants/opportunity-stage-options.ts` exporting `OPPORTUNITY_STAGE_FIELD_UNIVERSAL_IDENTIFIER` and an `OPPORTUNITY_STAGE_OPTIONS` catalog (stock stages + **Done** / **Dead**). - **`deals-board.view.ts`** imports the shared field id instead of inlining `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`. - The catalog is **not** synced by the app manifest — it documents planned option ids for scripts/reference only. **Done / Dead on prod:** added manually in Settings → Objects → Opportunity → Stage (not via app sync). `defineField` extension of the standard `stage` field was attempted and rejected (`FIELD_ALREADY_EXISTS`). Deals kanban groups in the manifest stay NEW–CUSTOMER only; Done/Dead columns appear when the workspace has those options. **Prod data cleanup (outside this PR):** all opportunities moved to **Done** except `ed95573c-cbe6-4dcc-a459-5369a7449636` (Aranya CRM Migration, kept in **New**). ### 2. Partners per Stage view — TABLE with stage grouping Replaces the old KANBAN board with a **grouped TABLE** (same pattern as Partners per Country / Applications): - **Type:** `TABLE` (was `KANBAN`) - **Group by:** `validationStage` — Application, Potential, Validated, Former, Rejected - **Columns:** Name, Country, Categories (`partnerScope`), Tier (`partnerTier`) - **Nav icon:** `IconTable` View / view-field / group universal ids were aligned to prod after KANBAN→TABLE install recreated the view (Twenty cannot change view type in place). ## Version **`1.1.9`** in this branch. Pre-merge deploys to `partner-twenty-com` went through **1.1.5 → 1.1.11** while iterating on the view; prod may be ahead of this branch’s pinned view id — one more id-alignment install after merge may be needed if nav doesn’t land on the grouped table. ## Files touched (twenty-partners only) | File | Change | |---|---| | `src/constants/opportunity-stage-options.ts` | New — stage field id + option catalog | | `src/views/deals-board.view.ts` | Import shared stage field constant | | `src/views/partners-per-stage.view.ts` | KANBAN → grouped TABLE + columns | | `src/navigation-menu-items/partners-per-stage.navigation-menu-item.ts` | Icon → `IconTable` | | `package.json` | **1.1.9** | ## Test plan - [x] `yarn lint` in `packages/twenty-apps/internal/twenty-partners` — 0/0 - [x] Prod: Done/Dead stage options present; Deals kanban shows 7 columns, no duplicates - [x] Prod: Partners per Stage — TABLE grouped by validation stage with Name / Country / Categories / Tier - [x] Prod: Opportunity cleanup — 20 → Done, 1 stays New (Aranya CRM Migration) - [ ] After merge + install on fresh workspace: Partners per Stage grouping renders without manual view fixes |
||
|
|
6ae6703e7d |
Map TFT useCase to partners need on opportunity import (#22054)
## Summary
- Accept optional `useCase` in the TFT → partners
`import-opportunity-from-tft` webhook payload
- Map it to `Opportunity.need` on create (TFT Use Case → partners Needs)
- Add unit tests for happy path and null `useCase` handling
- Bump twenty-partners to 1.1.4 (patch)
## Test plan
- [x] `yarn test:unit` (43 tests passing)
- [x] `yarn lint` (0 errors)
- [ ] Deploy to local/partners workspace with `yarn twenty dev --once`
- [ ] POST smoke test with `useCase` in body; confirm **Need** field
populated
- [ ] TFT workflow: add `"useCase": "{{record.useCase}}"` to HTTP body
(manual)
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22054?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. -->
|
||
|
|
9a62cb5a67 | add meeting bot app cover (#22049) | ||
|
|
5e8932001c |
Simplify Recall webhook logic function config (#22026)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22026?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. --> |
||
|
|
ceecf33555 |
fix(twenty-partners): restore partner side panel field visibility (#22024)
## Summary - Restores 17 partner profile fields in the `FIELDS_WIDGET` view backing the Partner record page side panel (My Profile, admin partner views). - Read-locks `validationStage` and `partnerTier` for the Partner role so admins still see them on the record page but partners do not on My Profile. - Renames legacy `profilePicture` label to "Profile Picture (legacy)" to distinguish from the new file field. - Bumps `twenty-partners` to **1.1.3** (already deployed to prod). ## Test plan - [ ] `yarn lint` in `packages/twenty-apps/internal/twenty-partners` — 0 errors - [ ] `yarn twenty dev --once` on a local workspace — sync succeeds - [ ] As admin: open a Partner record → side panel shows all profile fields including validationStage and partnerTier - [ ] As Partner role (My Profile): side panel shows profile fields but **not** validationStage or partnerTier - [ ] Upgrade path: install v1.1.3 on an existing workspace — view fields update in place <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22024?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. --> |
||
|
|
bab71afe54 |
fix(deps): bump vitest to 4 in twenty-meeting-bot (drops vulnerable esbuild) (#22025)
## Summary Bumps **vitest** `^3.1.1 → ^4.1.9` in `twenty-meeting-bot`, which lets **vite** resolve to **8.0.16** — and vite 8 dropped esbuild entirely (moved to rolldown). That **removes the vulnerable transitive `esbuild@0.27.7` outright**, resolving [Dependabot alert #1470](https://github.com/twentyhq/twenty/security/dependabot/1470) — GHSA-g7r4-m6w7-qqqr (esbuild dev-server arbitrary file read on Windows, `>=0.27.3 <0.28.1`). ## Why a parent-bump, not a resolution - The vulnerable esbuild came from `vite@7.3.5` (`esbuild ^0.27.0`, a `0.x` caret capped at `<0.28` — so `yarn up` couldn't reach the fix). - vite is gated by vitest's vite range: vitest **3.x** allows only `^5||^6||^7` (caps vite at 7 → esbuild 0.27); vitest **4.x** allows `^8`, and **vite 8 has no esbuild dependency at all**. - So bumping vitest lets vite resolve to 8, which **eliminates the vulnerable dependency entirely** — no `resolutions` entry to force or maintain. (Matches the repo's stated preference: fix by upgrading the parent, not by resolution.) ## Verification - `yarn install` — vite resolves to `8.0.16`; all `@esbuild/*@0.27.7` platform packages pruned; the only esbuild left is `0.28.1` (already-fixed, from another consumer). - `yarn typecheck` — passes. - `yarn test:unit` — **202 tests / 30 files pass** under vitest 4.1.9, no peer warnings; `vite-tsconfig-paths` still compatible with vite 8. - `yarn install --immutable` — passes. - (Integration `yarn test` is gated on a live Twenty server, so not run here — that requirement is independent of this bump.) - Separate yarn project — changes are confined to `twenty-meeting-bot/{package.json,yarn.lock}`; no root impact. ## Note vite 8 supports tsconfig-paths resolution natively (`resolve.tsconfigPaths: true`), so `vite-tsconfig-paths` could be dropped in a follow-up — left as-is to keep this change minimal. |
||
|
|
9b2ca6f3f7 |
bump sdk and app version for call recording bot app (#22019)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22019?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. --> |
||
|
|
99e7c2cde0 | Improve meeting bot recording tab layout and transcript speakers ui (#22016) | ||
|
|
4f1ffa0a96 |
fix(twenty-partners): coerce null fields in TFT opportunity import (#22017)
## What The TFT `HTTP Request` action POSTs `null` for empty fields (e.g. `amountMicros:null`, `closeDate:null`). The import schema typed those as `z.number()/z.string().optional()`, which reject `null` (it is not `undefined`), so the endpoint returned `ok:false / invalid_input` before any API call. ## Fix A `dropNulls` preprocessor on the request schema converts `null` (top-level or nested) to "field absent" before validation. Null optional fields are simply omitted from the created opportunity; required `name` still fails correctly if null. No schema-shape or behaviour-contract change. ## Tests Added a case feeding the failing payload shape (`amountMicros:null`, `closeDate:null`) → `created:true` with `amount`/`closeDate` omitted. 42/42 unit pass, lint clean. Patch bump `1.1.1 → 1.1.2`. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22017?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. --> |
||
|
|
c52c983b90 |
Source app About description from README and improve internal app READMEs (#22012)
## What - The SDK manifest build now sources an app's `aboutDescription` (the long-form "About" tab content) from its `README.md`. An explicit `aboutDescription` in the config still wins, matching the existing marketplace CDN fallback. - Removed the now-duplicated `aboutDescription` from internal app configs and deleted the standalone `ABOUT_DESCRIPTION` constant files. - Rewrote internal app READMEs to read as user-facing About content: stripped developer/build/source-path noise, and expanded the thin ones. `call-recording` and `self-hosting` (one-liners over substantial apps) and `people-data-labs` were rewritten from a close reading of the code; `twenty-exa` was verified for accuracy. - Added a unit test (and a fixture README) covering README → `aboutDescription` in the build. ## Why The README and the About description were maintained separately and drifted. Making the README the single source keeps the About tab accurate and removes duplicated copy. ## Notes for reviewers - Internal apps depend on the published `twenty-sdk`, so the build change takes effect for them after an SDK release + dependency bump. Until then, published apps still get README → `aboutDescription` via the marketplace CDN sync. - Standard/Custom app descriptions are unchanged (they are resolved in the frontend, not via the manifest). <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22012?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. --> |
||
|
|
e1c962acba |
feat(meeting-bot): configure Recall recording retention hours (#21978)
## Summary
- Sends an explicit Recall.ai recording retention policy when creating
or rescheduling meeting bots.
- Uses the optional server variable
`MEETING_BOT_RECORDING_RETENTION_HOURS` instead of a workspace/app
variable.
- Defaults to `166` hours (6 days and 22 hours), keeping Twenty-hosted
deployments below Recall.ai's 7-day free storage window while still
allowing self-hosters to configure a longer retention period.
## Why
Recall.ai accounts created after June 12, 2025 retain recording media
forever unless retention is configured. Twenty ingests the meeting
artifacts into its own storage, so Recall.ai media retention should be
bounded by default to avoid unnecessary third-party storage cost.
## Changes
- Replaces the days-based app variable with the server variable
`MEETING_BOT_RECORDING_RETENTION_HOURS`.
- Adds a default retention constant of `166` hours.
- Builds `recording_config.retention = { type: 'timed', hours }`
centrally through `getRecallBotRecordingConfig()`.
- Applies the same recording config to both bot creation and bot
rescheduling.
- Documents the server variable and warns that values above `168` hours
may incur Recall.ai storage charges.
- Updates Recall API tests to assert retention is sent and invalid
values fall back to the safe default.
## QA
- [x] `yarn test:unit`
- [x] `yarn lint`
- [x] `yarn exec tsc --noEmit -p tsconfig.spec.json`
- [x] `git diff --check`
- [x] Live Recall.ai bot payload includes `recording_config.retention =
{ type: 'timed', hours: 166 }`
---------
Co-authored-by: Emmanuel Hernandez <emmanuel.hernandez@clickbalance.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: ehconitin <nitinkoche03@gmail.com>
|
||
|
|
6f50b9d01e |
fix(twenty-partners): update Partner view in place on upgrade instead of deleting (#21995)
## Why Upgrading the already-installed `twenty-partners` app in place (0.5.x → 1.x) via `yarn twenty app:install` aborts during the sync reconcile: ``` view: INVALID_VIEW_DATA: Cannot delete the only view for this object (379b11d5-…) viewField: INVALID_VIEW_DATA: Label identifier view field cannot be deleted (21afcc69-…) ``` The marketplace-v2 change deleted `all-partners.view.ts`. On an installed workspace that view is the Partner object's primary view and holds the **label-identifier** viewField (the `name` column). Twenty's manifest sync refuses to delete an object's *only* view or a label-identifier viewField, so the in-place upgrade fails. (Fresh installs are unaffected; only upgrades from a version that had `all-partners` hit this.) ## What Repurpose the retired `all-partners` identity for `partners-validated` so the sync performs an **update in place** instead of a delete: - `partners-validated.view.ts` now uses the old view id `379b11d5-…`, and its `name` column reuses the old label viewField id `21afcc69-…`. - Remove the now-dangling `ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER` constant (its file was already gone). - Patch bump `1.1.0` → `1.1.1`. The resulting view is the intended "Partners Validated"; the other retired Partner view (`validated-partners`) deletes cleanly because the object keeps other views. ## Revision **Patch** — migration bugfix, no new behaviour. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21995?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. --> |
||
|
|
c7ad1ff8ee |
Route Recall webhooks by workspace metadata (#21991)
<!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21991?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. --> |
||
|
|
50c4659cae |
feat(twenty-partners): import an opportunity from TFT via manual workflow (#21979)
## What
Adds a one-way, manual copy of a single Opportunity from the
**twentyfortwenty (TFT)** workspace into **partners**. No automatic/echo
sync — one record per button press.
A TFT-side **manual Workflow** (a "Run workflow" button on the
Opportunity record) → **HTTP Request** action → `POST /s/opportunities`
on the partners app. The new `import-opportunity-from-tft` logic
function:
- **Shared-secret guard** on the `x-application-secret` header vs the
existing `PARTNER_APPLICATION_SECRET` app variable (the SDK's
`isAuthRequired` only accepts user JWTs, not API keys — same pattern as
`submit-partner-application`).
- **Idempotent on `tftOpportunityId`** (a field the partners Opportunity
object already has); falls back to `name` for manual calls. Re-press →
no duplicate.
- **Find-or-create** the Company (by name) and the point-of-contact
Person (by primary email), then `createOpportunity` with `name / amount
/ closeDate / stage / companyId / pointOfContactId`.
## Out of scope
- The **TFT-side workflow is a UI step** (built once in the TFT
workspace) — it can't live in this repo. The exact HTTP action config +
body mapping lives in the workflow itself.
- Owner/workspace-member copy and stage-enum remapping are intentionally
skipped (YAGNI).
## Files
- `src/logic-functions/import-opportunity-from-tft.logic-function.ts` —
the handler + manifest.
- `src/logic-functions/__tests__/import-opportunity-from-tft.test.ts` —
unit tests (auth reject / idempotent / mapped create).
- `package.json` — version bump **0.5.5 → 0.6.0** (minor; new feature).
## Verification (local bundle)
- `yarn test:unit` 3/3 · `yarn lint` 0/0 · `yarn twenty dev --once` →
`created logicFunction import-opportunity-from-tft`, no manifest
warnings.
- Live `POST /s/opportunities` with the secret → `201
{ok:true,created:true,id}` (confirms the app role can create Opportunity
+ Company + Person). Re-POST same `tftOpportunityId` → `created:false`.
Wrong secret → `unauthorized`.
## Deploy note
Additive (new logic function + HTTP route) — upgrades cleanly with
`deploy` + `install`. `PARTNER_APPLICATION_SECRET` is already set on
prod, so no new application variable to configure.
|
||
|
|
24533b510c |
feat(twenty-partners): marketplace v2 — Application-driven matching workspace (#21816)
## Summary Restructures the Twenty Partners app into an **Application-driven matching workspace**: leads post briefs (Opportunities), partners browse and **self-apply**, admins review applications and assign a winner. The candidacy funnel lives on `Application.state`; the deal lifecycle lives on the stock Opportunity `stage`. Version **1.0.0** — **breaking**: removes the legacy `matchStatus` field and the auto-match flow. Prod upgrade path is **uninstall → deploy → install** (not an in-place upgrade). ## How "Apply" works — no workflow, no special permission Partners apply by **creating an Application directly** from a listed brief (a normal record write, governed by the Application object permission). The `on-application-created` logic function (shipped in the manifest) then resolves the partner from `createdBy`, sets `state = APPLIED`, stamps `partner`/`partnerUser`/`lastActivityAt`, and dedupes by (opportunity, partner). - **No `WORKFLOWS` permission flag.** An earlier iteration used a manual "Apply" workflow, but running a manual workflow requires the `WORKFLOWS` flag, which **cannot be granted on an app-owned role** (the manifest sync drops `role → permissionFlag` links, and the metadata API rejects out-of-band grants on app roles). Self-apply via record-create sidesteps this entirely and is prod-viable as-is. - `Application.state` **defaults to `APPLIED`** so a partner never sees a misleading "Invited" flicker while the async handler runs. Admin invites set `INVITED` explicitly. ## ⚠️ Manual setup after install (per workspace) 1. **`yarn rls:configure`** — applies the partner row-level predicates and verifies field-locks (predicates can't ship in the manifest). Required for partner scoping. 2. **"Mark as Winner" workflow** — one manual-trigger workflow on **Application** → *Update Record* that sets `Opportunity.partner`, which drives the WON/BACKUP cascade. Admins run it (admins bypass the flag via `canUpdateAllSettings`); equivalent to editing the Opportunity's `partner` field directly. Steps in `src/workflows/README.md` (the **Apply** section there is superseded by self-apply). ## What's included - **Data model:** new `BACKUP` Application state; symmetric cascade owned by `on-opportunity-partner-won` — assign → winner `WON`, other applicants `BACKUP`; unassign → all reopen to `APPLIED`. `Opportunity.partner` is the single source of truth. - **Removed:** `matchStatus` field + `on-opportunity-auto-match` (dead). Deal lifecycle now on the stock `stage`. - **Partner row-level security (B7):** RLS predicates scope partners to their own `Partner`/`Person`/`Company`/`Application` rows; `Opportunity` is `(partnerUser IS me) OR (isListed = true)` so listed briefs are visible to all partners; `Application` is `(partnerUser IS me) OR (lastActivityAt IS EMPTY)` — the IS-EMPTY branch lets a partner's own insert pass (partnerUser is stamped just after insert). Field-locks make Opportunity `stage`/`amount` and most Application fields read-only for partners (pitch stays editable). Applied via `yarn rls:configure`. - *Trade-off:* an unstamped application (lastActivityAt null) is briefly readable by any partner — sub-second window, permanent only if the handler fails to stamp. Acceptable for an internal marketplace; the front-component Apply path (below) would remove it. - **Idempotency:** `on-application-created` dedupes duplicate applications by (opportunity, partner). - **Views & navigation**, reorganized into sections: - **Partner Workspace:** Open Briefs · My Applications · My Profile · My Deals - **Matching Admin:** Briefs to Match · Deals (board) · Applications · Applications by Opportunity · All Opportunities · Follow-up Applications · Follow-up Briefs - **Partners:** per Stage · per Country · Partner Applications · Validated (per-group COUNT) - Opportunity & Partner record **side panels** via FIELDS_WIDGET views (surface relations incl. `applications`, so a brief shows all its applications). ## Known issues / follow-ups - **Pre-existing failing unit test (not introduced here):** `on-partner-application-created › "posts a Discord embed when an APPLICATION-sourced partner is created"` — the handler/test are byte-identical to base; tracked separately. - **Follow-up views** lack the "older than 7 days" staleness filter — no confirmed relative date operand in this Twenty version (TODOs left in the views). - **Apply UX (future):** a front-component "Apply" button on the brief, calling an authenticated `/s/apply-to-brief` logic function (runs as the app), would replace the "create a record" entry point — nicer UX, and it removes the RLS IS-EMPTY trade-off. Not required to ship. ## Testing - Partner self-apply verified end-to-end as a partner (create application from a brief → lands on `APPLIED`). - Unit tests for the WON/BACKUP cascade + application handlers pass (the one failing test above is the pre-existing, unrelated Discord handler). - Lint clean (`oxlint`). <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21816?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-light.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
1646bdf35e |
Add twenty-partners on internal ci apps (#21975)
as title <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21975?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. --> |
||
|
|
2dc6034a6c |
Add twenty meeting bot to internal application ci (#21974)
- remove useless github-connector (validated with @charlesBochet) - add twenty-meeting-bot to internal apps |
||
|
|
6d7380dfec |
Remove unused call-recording application (#21966)
as title <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21966?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. --> |
||
|
|
6eb60f8a49 |
Add gallery screenshot to People Data Labs app (#21960)
Adds a marketplace gallery screenshot to the People Data Labs app. - Adds `public/gallery/cover.png` (Companies table with enriched fields) - References it via a new `screenshots` field in `application-config.ts`, matching the convention used by the other internal apps (Linear, Fireflies, Last Contact). <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21960?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. --> |
||
|
|
3030b7d0e5 |
Add people-data-labs on internal ci apps (#21941)
Add people-data-labs to internal apps CI: remove from CI_EXCLUDED_APPLICATIONS and unify config with twenty-discord/twenty-slack. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21941?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. --> |