nx_swcompare_crm.nx source
↩ module page · 233 lines · 21752 B
1// nx_swcompare_crm.nx -- MEASURED scoreboard for NISHI CRM / RELATIONSHIPS. Operator 2026-07-08:
2// "clients like andelinwest.com use a state of the art crm ... more than just crm but also a personal and a
3// professional aka network of people so relationships across everything can be managed -- even church or other orgs".
4// Grades the LIVE relationship surface across 34 axes, NEVER self-scored: each EXCEEDS/PRESENT cell is PROVEN by a
5// distinctive symbol in the REAL shipped organ source; an ABSENT (ROADMAP) cell requires the symbol to NOT exist =
6// an honest gap (the worklist). Liar-killed BOTH directions. Yardsticks = the SOTA field: Salesforce/HubSpot/
7// Pipedrive/Attio/Folk (pro CRM), Monica/Clay/Dex/Cloze (personal PRM), Planning Center/Breeze/Bloomerang (org/church/
8// nonprofit); bar = knowledge/compare/crm-bar.txt.
9// CORE RELATIONSHIP DATA A1..A8 ENTITY GRAPH B1..B6 PRO CRM C1..C6 ORG/COMMUNITY D1..D5 INTEGRATION E1..E9
10// SNAPSHOT ORACLE: exceeds==1, present==33, absent==0 (of 34) = FULL PRESENCE (not depth-parity -- the banner says so).
11// [r1 nx_relate B1-6+E5; r2 nx_crm C1-6; r3 nx_org D1-5; r4 nx_send_merge E4 + nx_relate_import E6; r5 nx_crm_acl E7 + nx_crm_flow E8] The 1 EXCEEDS = sovereign additive never-lose history
12// (seg_store, tombstone-not-delete, on your hardware). PRESENT = the Vizsla PRM primitives (profile/graph/timeline/
13// segments/dedup/reports/health) + the send/calendar integration. The whole entity graph + pro CRM + org/community +
14// multi-tenant is the ROADMAP. A rung re-grades its axis (living map): nx_relate/nx_crm/nx_org auto-flip on their symbols.
15// Two modes: no-arg = console census + liar-kill gate (run-exit 0 GREEN); `html` = emit the /compare/crm page.
16// Self-contained (only nx_syscalls). expect_exit: 0 license_tier: ORIGINAL
17import "nx_syscalls.nx"
18const K_MAGIC_262144: i64 = 262144
19
20func p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
21func pn(v: i64) -> i64 {
22 var m: i64 = v; if m < 0 { p("-" as *u8); m = 0 - m }
23 let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
24 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
25 let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k-1-i]; i = i + 1 } sys_write(1, o, k); return 0
26}
27func seq(a: *u8, b: *u8) -> i64 {
28 var i: i64 = 0
29 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
30 if b[i] != (0 as u8) { return 0 }
31 return 1
32}
33func lc_read(path: *u8, buf: *u8, cap: i64) -> i64 {
34 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 - 1 }
35 var tot: i64 = 0
36 while tot < cap { let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot); if r <= 0 { break } tot = tot + r }
37 sys_close(fd); return tot
38}
39func lc_has(buf: *u8, n: i64, needle: *u8) -> i64 {
40 var i: i64 = 0
41 while i < n { var k: i64 = 0; var hit: i64 = 1
42 while needle[k] != (0 as u8) { if i + k >= n { hit = 0; k = 0 - 1; break } if buf[i+k] != needle[k] { hit = 0; break } k = k + 1 }
43 if hit == 1 { if k > 0 { return 1 } } i = i + 1 }
44 return 0
45}
46func lc_file_has(path: *u8, needle: *u8, buf: *u8, cap: i64) -> i64 {
47 let n: i64 = lc_read(path, buf, cap); if n <= 0 { return 0 }
48 return lc_has(buf, n, needle)
49}
50func clsname(c: i64) -> *u8 { if c == 2 { return "EXCEEDS" as *u8 } if c == 1 { return "PRESENT" as *u8 } return "ROADMAP" as *u8 }
51func clscolor(c: i64) -> *u8 { if c == 2 { return "rgb(34,160,90)" as *u8 } if c == 1 { return "rgb(40,110,210)" as *u8 } return "rgb(120,124,134)" as *u8 }
52func nishi_note(c: i64) -> *u8 { if c == 2 { return "Sovereign edge" as *u8 } if c == 1 { return "Yes" as *u8 } return "Planned" as *u8 }
53func grpname(g: i64) -> *u8 {
54 if g == 0 { return "Core relationship data (people, graph, history)" as *u8 }
55 if g == 1 { return "Unified entity graph (people + companies + orgs + households)" as *u8 }
56 if g == 2 { return "Professional CRM (pipeline, deals)" as *u8 }
57 if g == 3 { return "Organizations, community, church, nonprofit" as *u8 }
58 return "Integration + platform" as *u8
59}
60func con_row(id: *u8, label: *u8, sota: *u8, cls: i64) -> i64 {
61 p(" [" as *u8); p(clsname(cls)); p("] " as *u8); p(id); p(" " as *u8); p(label); p(" vs " as *u8); p(sota); p("\n" as *u8); return 0
62}
63func emit_row(id: *u8, label: *u8, sota: *u8, cls: i64) -> i64 {
64 p("<tr><td class='cap'><span class='id'>" as *u8); p(id); p("</span> " as *u8); p(label)
65 p("</td><td class='ni'>" as *u8); p(nishi_note(cls))
66 p("</td><td class='so'>" as *u8); p(sota)
67 p("</td><td><span class='pill' style='background:" as *u8); p(clscolor(cls)); p("'>" as *u8); p(clsname(cls)); p("</span></td></tr>\n" as *u8)
68 return 0
69}
70
71func main(argc: i64, argv: *i64) -> i64 {
72 let cap: i64 = K_MAGIC_262144
73 let buf: *u8 = sys_mmap(cap)
74 let N: i64 = 34
75
76 let gid: *i64 = sys_mmap(8 * 40) as *i64
77 let glab: *i64 = sys_mmap(8 * 40) as *i64
78 let ginc: *i64 = sys_mmap(8 * 40) as *i64
79 let gpath: *i64 = sys_mmap(8 * 40) as *i64
80 let gneed: *i64 = sys_mmap(8 * 40) as *i64
81 let ggrp: *i64 = sys_mmap(8 * 40) as *i64
82 let gexc: *i64 = sys_mmap(8 * 40) as *i64
83 let gread: *i64 = sys_mmap(8 * 40) as *i64
84 let gcls: *i64 = sys_mmap(8 * 40) as *i64
85
86 // ---- CORE RELATIONSHIP DATA (Vizsla PRM + send) ----
87 gid[0]="CRM-A1" as *u8 as i64; glab[0]="Person / contact profile" as *u8 as i64; ginc[0]="Monica / Cloze" as *u8 as i64; gpath[0]="runtime/_hdl_build/nx_vizsla_contact.nx" as *u8 as i64; gneed[0]="VIZSLA-CONTACT-PROFILE" as *u8 as i64; ggrp[0]=0; gexc[0]=0
88 gid[1]="CRM-A2" as *u8 as i64; glab[1]="Relationship graph (intro paths between people)" as *u8 as i64; ginc[1]="ahead of most CRMs" as *u8 as i64; gpath[1]="runtime/_hdl_build/nx_vizsla_graph.nx" as *u8 as i64; gneed[1]="VIZSLA-GRAPH-PATH" as *u8 as i64; ggrp[1]=0; gexc[1]=0
89 gid[2]="CRM-A3" as *u8 as i64; glab[2]="Interaction timeline / history" as *u8 as i64; ginc[2]="Cloze / Dex" as *u8 as i64; gpath[2]="runtime/_hdl_build/nx_vizsla_log.nx" as *u8 as i64; gneed[2]="VIZSLA-LOG-TIMELINE" as *u8 as i64; ggrp[2]=0; gexc[2]=0
90 gid[3]="CRM-A4" as *u8 as i64; glab[3]="Segments / lists / tags" as *u8 as i64; ginc[3]="all" as *u8 as i64; gpath[3]="runtime/_hdl_build/nx_vizsla_segment.nx" as *u8 as i64; gneed[3]="VIZSLA-SEGMENT-VERDICT" as *u8 as i64; ggrp[3]=0; gexc[3]=0
91 gid[4]="CRM-A5" as *u8 as i64; glab[4]="Dedup / merge duplicate contacts" as *u8 as i64; ginc[4]="all" as *u8 as i64; gpath[4]="runtime/_hdl_build/nx_vizsla_dedup.nx" as *u8 as i64; gneed[4]="VIZSLA-DEDUP-VIEW-SUMMARY" as *u8 as i64; ggrp[4]=0; gexc[4]=0
92 gid[5]="CRM-A6" as *u8 as i64; glab[5]="Reports / analytics" as *u8 as i64; ginc[5]="Salesforce" as *u8 as i64; gpath[5]="runtime/_hdl_build/nx_vizsla_report.nx" as *u8 as i64; gneed[5]="VIZSLA-REPORT-VERDICT" as *u8 as i64; ggrp[5]=0; gexc[5]=0
93 gid[6]="CRM-A7" as *u8 as i64; glab[6]="Keep-in-touch cadence / relationship health" as *u8 as i64; ginc[6]="Dex / Clay" as *u8 as i64; gpath[6]="runtime/_hdl_build/nx_vizsla_health.nx" as *u8 as i64; gneed[6]="VIZSLA-HEALTH-VERDICT" as *u8 as i64; ggrp[6]=0; gexc[6]=0
94 gid[7]="CRM-A8" as *u8 as i64; glab[7]="Structured postal / contact fields" as *u8 as i64; ginc[7]="all" as *u8 as i64; gpath[7]="runtime/nx_send.nx" as *u8 as i64; gneed[7]="POSTAL-ADDR" as *u8 as i64; ggrp[7]=0; gexc[7]=0
95
96 // ---- UNIFIED ENTITY GRAPH (roadmap -> nx_relate.nx) ----
97 gid[8]="CRM-B1" as *u8 as i64; glab[8]="Typed entities (person/company/org/household)" as *u8 as i64; ginc[8]="Attio / Salesforce" as *u8 as i64; gpath[8]="runtime/nx_relate.nx" as *u8 as i64; gneed[8]="ENTITY-TYPE" as *u8 as i64; ggrp[8]=1; gexc[8]=0
98 gid[9]="CRM-B2" as *u8 as i64; glab[9]="Typed relationships / edges (works-at, member-of, family)" as *u8 as i64; ginc[9]="Attio" as *u8 as i64; gpath[9]="runtime/nx_relate.nx" as *u8 as i64; gneed[9]="REL-EDGE" as *u8 as i64; ggrp[9]=1; gexc[9]=0
99 gid[10]="CRM-B3" as *u8 as i64; glab[10]="Roles on a relationship (title / position / ministry)" as *u8 as i64; ginc[10]="Salesforce NPSP" as *u8 as i64; gpath[10]="runtime/nx_relate.nx" as *u8 as i64; gneed[10]="REL-ROLE" as *u8 as i64; ggrp[10]=1; gexc[10]=0
100 gid[11]="CRM-B4" as *u8 as i64; glab[11]="Context tag (personal / professional / church / civic)" as *u8 as i64; ginc[11]="Folk" as *u8 as i64; gpath[11]="runtime/nx_relate.nx" as *u8 as i64; gneed[11]="REL-CONTEXT" as *u8 as i64; ggrp[11]=1; gexc[11]=0
101 gid[12]="CRM-B5" as *u8 as i64; glab[12]="Households / families as entities" as *u8 as i64; ginc[12]="Planning Center / NPSP" as *u8 as i64; gpath[12]="runtime/nx_relate.nx" as *u8 as i64; gneed[12]="HOUSEHOLD" as *u8 as i64; ggrp[12]=1; gexc[12]=0
102 gid[13]="CRM-B6" as *u8 as i64; glab[13]="Custom fields / flexible schema" as *u8 as i64; ginc[13]="Attio" as *u8 as i64; gpath[13]="runtime/nx_relate.nx" as *u8 as i64; gneed[13]="CUSTOM-FIELD" as *u8 as i64; ggrp[13]=1; gexc[13]=0
103
104 // ---- PROFESSIONAL CRM (roadmap -> nx_crm.nx) ----
105 gid[14]="CRM-C1" as *u8 as i64; glab[14]="Companies / accounts" as *u8 as i64; ginc[14]="all" as *u8 as i64; gpath[14]="runtime/nx_crm.nx" as *u8 as i64; gneed[14]="ACCOUNT" as *u8 as i64; ggrp[14]=2; gexc[14]=0
106 gid[15]="CRM-C2" as *u8 as i64; glab[15]="Deals / opportunities" as *u8 as i64; ginc[15]="Salesforce / HubSpot" as *u8 as i64; gpath[15]="runtime/nx_crm.nx" as *u8 as i64; gneed[15]="OPPORTUNITY" as *u8 as i64; ggrp[15]=2; gexc[15]=0
107 gid[16]="CRM-C3" as *u8 as i64; glab[16]="Pipeline stages (kanban)" as *u8 as i64; ginc[16]="Pipedrive" as *u8 as i64; gpath[16]="runtime/nx_crm.nx" as *u8 as i64; gneed[16]="PIPELINE-STAGE" as *u8 as i64; ggrp[16]=2; gexc[16]=0
108 gid[17]="CRM-C4" as *u8 as i64; glab[17]="Tasks / activities on records" as *u8 as i64; ginc[17]="all" as *u8 as i64; gpath[17]="runtime/nx_crm.nx" as *u8 as i64; gneed[17]="ACTIVITY" as *u8 as i64; ggrp[17]=2; gexc[17]=0
109 gid[18]="CRM-C5" as *u8 as i64; glab[18]="Sequences / follow-up automation" as *u8 as i64; ginc[18]="HubSpot / Close" as *u8 as i64; gpath[18]="runtime/nx_crm.nx" as *u8 as i64; gneed[18]="SEQUENCE" as *u8 as i64; ggrp[18]=2; gexc[18]=0
110 gid[19]="CRM-C6" as *u8 as i64; glab[19]="Pipeline / forecast dashboards" as *u8 as i64; ginc[19]="Salesforce" as *u8 as i64; gpath[19]="runtime/nx_crm.nx" as *u8 as i64; gneed[19]="DASHBOARD" as *u8 as i64; ggrp[19]=2; gexc[19]=0
111
112 // ---- ORG / COMMUNITY / CHURCH / NONPROFIT (roadmap -> nx_org.nx) ----
113 gid[20]="CRM-D1" as *u8 as i64; glab[20]="Organization + member management" as *u8 as i64; ginc[20]="Planning Center" as *u8 as i64; gpath[20]="runtime/nx_org.nx" as *u8 as i64; gneed[20]="ORG-MEMBER" as *u8 as i64; ggrp[20]=3; gexc[20]=0
114 gid[21]="CRM-D2" as *u8 as i64; glab[21]="Groups / ministries / teams + roles" as *u8 as i64; ginc[21]="Planning Center" as *u8 as i64; gpath[21]="runtime/nx_org.nx" as *u8 as i64; gneed[21]="GROUP-ROLE" as *u8 as i64; ggrp[21]=3; gexc[21]=0
115 gid[22]="CRM-D3" as *u8 as i64; glab[22]="Attendance / check-in" as *u8 as i64; ginc[22]="Planning Center" as *u8 as i64; gpath[22]="runtime/nx_org.nx" as *u8 as i64; gneed[22]="ATTENDANCE" as *u8 as i64; ggrp[22]=3; gexc[22]=0
116 gid[23]="CRM-D4" as *u8 as i64; glab[23]="Giving / donations" as *u8 as i64; ginc[23]="Bloomerang" as *u8 as i64; gpath[23]="runtime/nx_org.nx" as *u8 as i64; gneed[23]="GIVING" as *u8 as i64; ggrp[23]=3; gexc[23]=0
117 gid[24]="CRM-D5" as *u8 as i64; glab[24]="Volunteers / serving" as *u8 as i64; ginc[24]="Planning Center" as *u8 as i64; gpath[24]="runtime/nx_org.nx" as *u8 as i64; gneed[24]="VOLUNTEER" as *u8 as i64; ggrp[24]=3; gexc[24]=0
118
119 // ---- INTEGRATION + PLATFORM ----
120 gid[25]="CRM-E1" as *u8 as i64; glab[25]="Reach any contact across channels" as *u8 as i64; ginc[25]="email in all; +physical Nishi" as *u8 as i64; gpath[25]="runtime/nx_send.nx" as *u8 as i64; gneed[25]="SEND-INTENT" as *u8 as i64; ggrp[25]=4; gexc[25]=0
121 gid[26]="CRM-E2" as *u8 as i64; glab[26]="Cross-channel delivery ledger" as *u8 as i64; ginc[26]="Salesforce activity" as *u8 as i64; gpath[26]="runtime/_hdl_build/nx_vizsla_notify.nx" as *u8 as i64; gneed[26]="VIZSLA-NOTIFY-DELIVER" as *u8 as i64; ggrp[26]=4; gexc[26]=0
122 gid[27]="CRM-E3" as *u8 as i64; glab[27]="Calendar / reminders / scheduling" as *u8 as i64; ginc[27]="all" as *u8 as i64; gpath[27]="runtime/_hdl_build/nx_vizsla_remind.nx" as *u8 as i64; gneed[27]="VIZSLA-REM-DUE" as *u8 as i64; ggrp[27]=4; gexc[27]=0
123 gid[28]="CRM-E4" as *u8 as i64; glab[28]="Bulk / mail-merge to a segment" as *u8 as i64; ginc[28]="all (email); +physical Nishi" as *u8 as i64; gpath[28]="runtime/nx_send_merge.nx" as *u8 as i64; gneed[28]="BULK-MERGE" as *u8 as i64; ggrp[28]=4; gexc[28]=0
124 gid[29]="CRM-E5" as *u8 as i64; glab[29]="Multi-tenant client workspaces (andelinwest)" as *u8 as i64; ginc[29]="every SaaS CRM" as *u8 as i64; gpath[29]="runtime/nx_relate.nx" as *u8 as i64; gneed[29]="TENANT" as *u8 as i64; ggrp[29]=4; gexc[29]=0
125 gid[30]="CRM-E6" as *u8 as i64; glab[30]="Import / export (CSV / vCard)" as *u8 as i64; ginc[30]="all" as *u8 as i64; gpath[30]="runtime/nx_relate_import.nx" as *u8 as i64; gneed[30]="IMPORT-EXPORT" as *u8 as i64; ggrp[30]=4; gexc[30]=0
126 gid[31]="CRM-E7" as *u8 as i64; glab[31]="Users + roles + record permissions" as *u8 as i64; ginc[31]="Salesforce" as *u8 as i64; gpath[31]="runtime/nx_crm_acl.nx" as *u8 as i64; gneed[31]="ACL-ROLE" as *u8 as i64; ggrp[31]=4; gexc[31]=0
127 gid[32]="CRM-E8" as *u8 as i64; glab[32]="Automation / workflow triggers" as *u8 as i64; ginc[32]="HubSpot Flow" as *u8 as i64; gpath[32]="runtime/nx_crm_flow.nx" as *u8 as i64; gneed[32]="WORKFLOW" as *u8 as i64; ggrp[32]=4; gexc[32]=0
128 gid[33]="CRM-E9" as *u8 as i64; glab[33]="Sovereign additive never-lose history (on your hardware)" as *u8 as i64; ginc[33]="cloud CRMs cannot" as *u8 as i64; gpath[33]="runtime/_hdl_build/nx_vizsla_notify.nx" as *u8 as i64; gneed[33]="nx_seg_store" as *u8 as i64; ggrp[33]=4; gexc[33]=1
129
130 var exceeds: i64 = 0
131 var present: i64 = 0
132 var absent: i64 = 0
133 var i: i64 = 0
134 while i < N {
135 let r: i64 = lc_file_has(gpath[i] as *u8, gneed[i] as *u8, buf, cap)
136 gread[i] = r
137 var cls: i64 = 0
138 if r == 1 {
139 if gexc[i] == 1 { cls = 2; exceeds = exceeds + 1 } else { cls = 1; present = present + 1 }
140 } else {
141 absent = absent + 1
142 }
143 gcls[i] = cls
144 i = i + 1
145 }
146 let cov: i64 = (present + exceeds) * 1000 / N
147
148 var html: i64 = 0
149 if argc > 1 { if seq(argv[1] as *u8, "html" as *u8) == 1 { html = 1 } }
150
151 if html == 1 {
152 p("<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'>" as *u8)
153 p("<title>Nishi Compare -- CRM and Relationships</title><style>" as *u8)
154 p(":root{color-scheme:dark light}" as *u8)
155 p("body{margin:0;background:rgb(13,15,20);color:rgb(228,230,235);font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;line-height:1.5}" as *u8)
156 p(".wrap{max-width:1000px;margin:0 auto;padding:28px 20px 64px}" as *u8)
157 p("a{color:rgb(120,170,255);text-decoration:none}" as *u8)
158 p("h1{font-size:30px;margin:6px 0 2px}.sub{color:rgb(150,155,165);font-size:15px;margin-bottom:20px}" as *u8)
159 p(".ban{background:rgb(20,23,30);border:1px solid rgb(38,42,52);border-radius:14px;padding:16px 18px;margin:14px 0 22px}" as *u8)
160 p(".big{font-size:22px;font-weight:700}.big b{color:rgb(90,200,140)}" as *u8)
161 p(".legend span{display:inline-block;margin-right:14px;font-size:13px;color:rgb(170,175,185)}" as *u8)
162 p(".dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:5px;vertical-align:middle}" as *u8)
163 p("table{width:100%;border-collapse:collapse;font-size:14px}" as *u8)
164 p("td{padding:9px 10px;border-bottom:1px solid rgb(30,34,43);vertical-align:top}" as *u8)
165 p(".grp td{background:rgb(18,21,27);color:rgb(150,200,255);font-weight:700;letter-spacing:.3px;font-size:13px;text-transform:uppercase}" as *u8)
166 p(".cap{width:46%}.id{color:rgb(110,116,128);font-size:11px;font-weight:700;margin-right:6px}" as *u8)
167 p(".ni{width:16%;color:rgb(200,205,214)}.so{width:22%;color:rgb(160,165,175)}" as *u8)
168 p(".pill{display:inline-block;padding:3px 9px;border-radius:999px;color:white;font-size:11px;font-weight:700;letter-spacing:.3px}" as *u8)
169 p(".foot{margin-top:26px;color:rgb(150,155,165);font-size:13px}.foot b{color:rgb(210,215,224)}" as *u8)
170 p("@media(prefers-color-scheme:light){body{background:rgb(250,250,252);color:rgb(20,24,32)}.ban{background:white;border-color:rgb(226,228,234)}.grp td{background:rgb(238,242,250);color:rgb(30,90,190)}td{border-color:rgb(232,234,240)}.ni{color:rgb(40,46,58)}.so{color:rgb(90,96,110)}}" as *u8)
171 p("</style></head><body><div class='wrap'>" as *u8)
172 p("<div class='sub'><a href='/compare'>Nishi Compare</a> / CRM and Relationships</div>" as *u8)
173 p("<h1>Nishi CRM and Relationships vs the field</h1>" as *u8)
174 p("<div class='sub'>One relationship graph across personal, professional, and organizations (family, business, church, clubs) -- mechanically measured by reading real organ source. Every contact reachable over email OR postcard, letter, card, or gift.</div>" as *u8)
175 p("<div class='ban'><div class='big'>Coverage <b>" as *u8); pn(cov); p(" / 1000</b></div>" as *u8)
176 p("<div class='sub' style='margin:6px 0 10px'>Capability PRESENCE across " as *u8); pn(N); p(" axes -- " as *u8)
177 pn(exceeds); p(" sovereign edge, " as *u8); pn(present); p(" present, " as *u8); pn(absent); p(" on the roadmap. The incumbents lead on CRM depth + org tooling; Nishi leads on sovereignty -- your relationships live on YOUR hardware, additive and never-lost.</div>" as *u8)
178 p("<div class='legend'><span><i class='dot' style='background:rgb(34,160,90)'></i>EXCEEDS -- sovereign edge</span><span><i class='dot' style='background:rgb(40,110,210)'></i>PRESENT -- built + gated</span><span><i class='dot' style='background:rgb(120,124,134)'></i>ROADMAP -- next rungs</span></div></div>" as *u8)
179 p("<table>" as *u8)
180 var g: i64 = 0 - 1
181 var j: i64 = 0
182 while j < N {
183 if ggrp[j] != g {
184 g = ggrp[j]
185 p("<tr class='grp'><td colspan='4'>" as *u8); p(grpname(g)); p("</td></tr>\n" as *u8)
186 }
187 emit_row(gid[j] as *u8, glab[j] as *u8, ginc[j] as *u8, gcls[j])
188 j = j + 1
189 }
190 p("</table>" as *u8)
191 p("<div class='foot'><b>The sovereign edge:</b> your entire relationship history is additive, never-lost, and on your own hardware (seg_store: append-only, tombstone-not-delete = tamper-evident) -- no cloud CRM guarantees that, and you own the data. Nishi also does relationship-graph intro-path finding, ahead of most CRMs. " as *u8)
192 p("<b>Present today:</b> rich contact profiles, the relationship graph, interaction timeline, segments, dedup/merge, keep-in-touch health -- and every contact is reachable over email OR postcard/letter/card/gift via nx_send, with calendar + reminders wired in. <b>The roadmap</b> is the unified entity graph (companies + organizations + households + typed relationships + context tags, so one model serves a business, a family, AND a church), the professional CRM pipeline, org/community management (members, groups, giving, attendance), and multi-tenant client workspaces. " as *u8)
193 p("Competitor rows reflect public product docs (2025-26); Nishi rows are live symbol reads of on-disk source. Generated by <b>nx_swcompare_crm</b>, liar-killed both directions.</div>" as *u8)
194 p("</div></body></html>\n" as *u8)
195 return 0
196 }
197
198 p("=== NX-SWCOMPARE-CRM -- Nishi CRM/relationships MEASURED vs Salesforce/HubSpot/Attio/Folk/Monica/Planning Center ===\n" as *u8)
199 var g2: i64 = 0 - 1
200 var k2: i64 = 0
201 while k2 < N {
202 if ggrp[k2] != g2 { g2 = ggrp[k2]; p("\n-- " as *u8); p(grpname(g2)); p(" --\n" as *u8) }
203 con_row(gid[k2] as *u8, glab[k2] as *u8, ginc[k2] as *u8, gcls[k2])
204 k2 = k2 + 1
205 }
206
207 let negctl: i64 = lc_file_has("runtime/_hdl_build/nx_vizsla_contact.nx" as *u8, "ZZQX_NOT_A_REAL_SYMBOL_QWERTY" as *u8, buf, cap)
208 let posctl: i64 = lc_file_has("runtime/_hdl_build/nx_vizsla_contact.nx" as *u8, "VIZSLA-CONTACT-PROFILE" as *u8, buf, cap)
209 let bar1: i64 = lc_file_has("knowledge/compare/crm-bar.txt" as *u8, "Salesforce" as *u8, buf, cap)
210 let bar2: i64 = lc_file_has("knowledge/compare/crm-bar.txt" as *u8, "Attio" as *u8, buf, cap)
211 let bar3: i64 = lc_file_has("knowledge/compare/crm-bar.txt" as *u8, "Monica" as *u8, buf, cap)
212 let bar4: i64 = lc_file_has("knowledge/compare/crm-bar.txt" as *u8, "Planning Center" as *u8, buf, cap)
213
214 var ok: i64 = 1
215 if exceeds != 1 { ok = 0 }
216 if present != 33 { ok = 0 }
217 if absent != 0 { ok = 0 }
218 if (exceeds + present + absent) != N { ok = 0 }
219 if negctl != 0 { ok = 0 }
220 if posctl != 1 { ok = 0 }
221 if bar1 != 1 { ok = 0 }
222 if bar2 != 1 { ok = 0 }
223 if bar3 != 1 { ok = 0 }
224 if bar4 != 1 { ok = 0 }
225
226 p("\nCRM-CENSUS coverage=" as *u8); pn(cov); p("/1000 exceeds=" as *u8); pn(exceeds)
227 p(" present=" as *u8); pn(present); p(" absent=" as *u8); pn(absent); p(" of=" as *u8); pn(N)
228 p(" negctl=" as *u8); pn(negctl); p(" posctl=" as *u8); pn(posctl)
229 p(" grounded=" as *u8); pn(bar1 + bar2 + bar3 + bar4)
230 if ok == 1 { p(" verdict=GREEN\n" as *u8); return 0 }
231 p(" verdict=RED\n" as *u8)
232 return 1
233}