## Summary Iterative refinements to the partner design-doc doctrine after running it on a second lead (TADA) and reviewing output side by side. Touches only the `twenty-partner-design-doc` skill files (doctrine + Claude Code wrapper); no runtime / app code. **What changed** - **Flag system:** emoji + short text label pairs only (`🔮 inf.`, **❓ open**, **⚠️ heavy**, **🛑 blocker**). Replaces the prior text-tag-only system; scannable, unambiguous. - **Section structure:** split into **Required** (always present) and **Conditional** (Views, Automations, Integrations, Reporting). Include conditional sections only when the client grounded them in the source. Number sequentially, no gaps. - **No filler placeholders:** banned `X was not named` / `left out on purpose` lists in body sections. Unknowns belong in Open questions, not as their own section or bullet. - **Functional cross-refs:** every `§N` reference is now a markdown anchor link `[§N](#n-section-slug)`, so a partner skimming the doc can navigate. Bare `§N` is banned. - **Bullets and tables over paragraphs**, with **Open questions** kept as a numbered list (so the partner can read items 1, 2, 3 with the client). - **Views & navigation** rendered as a tight `Surface | Shows | Audience` table. No view-type column — table / kanban / page layout is the partner's call, not a scoping decision. - **Data-model table** gains a `Source` column (`client` / `inf.`) for at-a-glance fact-vs-inference visibility. - **Business decisions over technical mechanics:** cut SDK / runtime internals that don't move the quote (Docker version, OAuth flavour, auto-system relations, env-var names, CI/CD workflow detail). - **Common-mistakes table** updated with rows for the new rules. - **SKILL.md self-check** expanded so the wrapper enforces all of the above before saving. ## Test plan - [ ] Re-read doctrine end-to-end for internal consistency - [ ] Verify the four canonical emoji + text pairs appear and no stray emoji flags remain - [ ] Confirm Required vs Conditional structure is internally consistent (no section listed in both) - [ ] Confirm functional-cross-ref rule appears in both Rules and Formatting and is reflected in the SKILL.md self-check - [ ] Confirm Views & navigation entry mandates the three-column table and bans a Type column - [ ] Confirm Common-mistakes table covers each new rule
twenty-partners
A Twenty app that turns the CRM into the operating system for the Twenty partner program: intake partner-eligible deals, match them to vetted marketplace partners, and track the matching pipeline end-to-end.
Built on Twenty with twenty-sdk v2.5.
What's inside
- Custom object:
Partner— slug, status, availability, served geos, languages spoken, deployment expertise, Calendly link, last-match timestamp. Seesrc/objects/partner.object.ts. - Opportunity extensions —
matchStatus,designDocStatus,introSentAt,lastRelanceSentAt,tftId, plus apartnerrelation. - Logic functions
on-opportunity-auto-match— fires whenmatchStatusis set toAUTO_MATCH. Assigns the longest-idle available partner and flips status toMATCHED. If no partner is available, hands off toMANUAL_MATCHwith an audit Note explaining why.list-available-partners— surfaces matchable partners for a given opportunity.post-install— first-run setup.
- Roles (
src/roles/)- Twenty Partner Ops — internal team role, full CRUD on Partner/Company/Person/Opportunity.
- Partner — placeholder external-partner role. Do not assign until Twenty ships row-level permissions — it currently grants access to every record.
- Views (
src/views/)Waiting for match— opportunities awaiting human action (matchStatusisTO_BE_MATCHEDorMANUAL_MATCH).Matches overview— full matching funnel grouped bymatchStatus(configure Kanban grouping manually in the UI).Opportunities— replacement of the native opportunities view with the partner columns.PartnersandAll matched deals— partner-side index and deal log.
- Sidebar nav — surfaced in workflow order:
Waiting for match,All partner deals,Matches overview,Partners,Opportunities. - Seed scripts (
src/scripts/) — populate a fresh workspace with realistic demo data.
Match status pipeline
matchStatus is a non-nullable SELECT field with a default of TO_BE_MATCHED. The 10 states follow the deal lifecycle:
| Status | Meaning |
|---|---|
TO_BE_MATCHED |
Default — deal entered, awaiting assignment |
MANUAL_MATCH |
Needs a human to pick a partner |
AUTO_MATCH |
Triggers automatic partner assignment |
MATCHED |
Partner assigned |
INTRODUCED_TO_A_PARTNER |
Customer intro sent |
WORKING_WITH_A_PARTNER |
Engagement underway |
IMPLEMENTING |
Active implementation |
WON |
Deal closed won |
RECONNECT_LATER |
Paused — reconnect in future |
LOST |
Deal closed lost |
Getting started
Requires a local Twenty server at http://localhost:2020 and Node ^24.5.
yarn install
yarn twenty dev
Default dev credentials: tim@apple.dev / tim@apple.dev.
Run yarn twenty help for the full CLI reference.
Common commands
| Command | What it does |
|---|---|
yarn twenty dev |
Start the dev server and sync the app on file changes |
yarn twenty server status |
Check the local Twenty server |
yarn lint / yarn lint:fix |
Run oxlint |
yarn test |
Run integration tests (vitest.config.ts) |
Seeding demo data
Two idempotent seed scripts. Both run via the vitest.seed.config.ts config that skips
the global app uninstall/reinstall.
# 1. Marketplace partners (run first — pipeline seed wires opportunities to these by slug)
yarn vitest run --config vitest.seed.config.ts src/scripts/seed-marketplace-partners.ts
# 2. Pipeline demo: 3 companies, 3 people, 15 opportunities spread across matchStatus values
yarn vitest run --config vitest.seed.config.ts src/scripts/seed-pipeline-demo.ts
Both scripts skip records that already exist (by slug, name, or firstName+lastName),
so they are safe to re-run.
Known limitations
Current SDK gaps blocking further polish:
- Custom Partner record page layout (RECORD_TABLE has no relation scoping).
- Native Opportunities view column-order override.
- Kanban view configuration from app code (
ViewType.KANBANis currently ignored). - App and field descriptions.