Vocabulary tables
59 vocabulary tables replace what would otherwise have been 59 Choice columns. There is no Choice column anywhere in the solution: every dropdown is a lookup, so administrators manage options in EPMS Admin without a deployment. Rows are deactivated, never deleted, so historic records keep resolving.
seco_code is what logic branches on. seco_legacyoptionvalue exists only so migration can map a legacy integer to a row. Neither is the row GUID, and the GUID is never used for anything.
Lookup row GUIDs are generated per environment and differ between Dev, Test and Prod. A plugin or flow that branches on a GUID passes every test in Dev and fails silently in Prod, because the row it matched no longer exists under that id. Resolve the lookup, read seco_code, branch on the code. This applies to all 55 vocabulary tables, both tiers.
13 vocabularies diverge between the prototypes — Platform 1 is authoritative
Platform 1 carries the fixed integers and states they are fixed. Platform 2 marks every inherited vocabulary UNCONFIRMED because Platform 1’s file was not supplied with its pack, so it invented plausible members and built against them. Ten to twelve years of migrated Smartsheet history carries Platform 1’s integers. Seed from Platform 1 throughout. Only four agree: seco_approvaltrack, seco_approvaldecision, seco_gatekind, seco_yesno.
| Vocabulary | Platform 1 (authoritative) | Platform 2 | Consequence |
|---|---|---|---|
| seco_restrictionkind | No · ITAR · Other restriction · NDA restricted (100000003) | No · ITAR · Other restriction | Platform 2 is missing NdaRestricted 100000003 entirely, and that is the December default. Any Platform 2 rule that defaults restriction handling defaults to a member that does not exist in the real set. |
| seco_accountcategory | Customer · Partner · Supplier | Customer · Prospect · MachineToolBuilder · Distributor · Internal | MachineToolBuilder and Distributor are channel concepts, not account categories, and seco_channel already exists as a reference table. Platform 2 conflated two ideas. |
| seco_resourcekind | Person · Equipment | Internal · Contractor · Partner · Equipment | Platform 2's cost-rate and capacity rules branch on members that do not exist. |
| seco_agreementkind | Purchase order · Letter of intent · Long-term agreement | StatementOfWork · Framework · NDA · Amendment | Completely disjoint. SOW is a separate table (seco_sow), so Platform 2 modelled it twice. |
| seco_proposalkind | Technical · Commercial | Budgetary · Firm · Revised | Disjoint. Nothing written against Platform 2's members resolves. |
| seco_participantrole | Customer contact · Partner contact · Observer | Sponsor · Engineer · Buyer · Other | Diverges — seed from Platform 1. |
| seco_feedbacksource | Customer · Salesperson | Survey · Meeting · Email · Portal | Diverges — seed from Platform 1. |
| seco_requestorigin | Opportunity · Internal · Portal | Internal · Portal · CRM · Integration | Diverges — seed from Platform 1. |
| seco_costcategory | Labour · Tooling · Materials · Travel · Subcontract · Equipment · Other | Labour · Material · Logistics · Overhead | Diverges — seed from Platform 1. |
| seco_language | English · German · French · Spanish · Swedish · Other | English · German · Swedish · Chinese | Diverges — seed from Platform 1. |
| seco_eventkind | Scope change · Specification change · Schedule change · Commercial change · Escalation · Other | Meeting · Milestone · Issue · Note | Diverges — seed from Platform 1. |
| seco_projectroute | Tooling led · Solution led · Back to sales · Application engineering | Standard · FastTrack · Strategic | Diverges — seed from Platform 1. |
| seco_syncstate | Not applicable · Pending · Synced · Failed · Superseded | Pending · Synced · Failed · Skipped | Diverges — seed from Platform 1. |
The two-tier split is confirmed.
Business tier and system tier are agreed, not proposed. Build against them.
Shared column contract — every vocabulary table
| seco_name | Text (100) — primary name | Label shown to users; administrators may rename on business-tier tables. |
| seco_code | Text (50) — unique, alternate key | Business logic branches on this, never on the GUID. |
| seco_description | Multiline text (2000) | Plain-English explanation of when the row applies. |
| seco_sortorder | Whole number | Display order in lookups and views. |
| seco_legacyoptionvalue | Whole number — not required | The fixed Dataverse option-set integer this row replaces. Migration maps a legacy integer to this row; nothing else reads it. Business logic branches on seco_code, never on this column and never on the row GUID. |
| seco_masteredby | Lookup → seco_masteredby | Which system owns the row: EPMS, D365 or Manual. |
| seco_crmreference | Text (100) | Matching key in D365 when the row is CRM-sourced. |
| seco_lastsyncedon | Date and time | Last successful synchronisation timestamp. |
| statecode | Standard state | Rows are deactivated, never deleted, so historic records keep resolving. |