@amityco/social-plus-vise 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +45 -2
  2. package/README.md +19 -9
  3. package/dist/capabilities.js +29 -1
  4. package/dist/entryState.js +71 -0
  5. package/dist/experience.js +70 -0
  6. package/dist/explore.js +1 -1
  7. package/dist/flow.js +382 -0
  8. package/dist/humanFormat.js +25 -0
  9. package/dist/intake.js +117 -0
  10. package/dist/intelligence/placement.js +2 -1
  11. package/dist/outcomes.js +126 -28
  12. package/dist/productExpectations.js +15 -0
  13. package/dist/requestReadiness.js +99 -0
  14. package/dist/server.js +566 -19
  15. package/dist/sidecar.js +5 -0
  16. package/dist/solutionPath.js +1 -1
  17. package/dist/tools/compliance.js +752 -125
  18. package/dist/tools/creative.js +14 -12
  19. package/dist/tools/design.js +321 -11
  20. package/dist/tools/experienceCompiler.js +1 -1
  21. package/dist/tools/experienceSensors.js +1 -1
  22. package/dist/tools/harness.js +13 -0
  23. package/dist/tools/integration.js +263 -90
  24. package/dist/tools/learning.js +1 -3
  25. package/dist/tools/project.js +963 -66
  26. package/dist/tools/smoke.js +134 -0
  27. package/dist/tools/uxHarness.js +54 -6
  28. package/dist/uikitCustomization.js +3 -1
  29. package/dist/version.js +7 -3
  30. package/package.json +1 -1
  31. package/packages/intelligence/catalog/catalog.schema.json +1 -1
  32. package/packages/intelligence/catalog/experience-objects.json +2 -2
  33. package/packages/intelligence/catalog/variants.json +24 -0
  34. package/rules/event.yaml +3 -0
  35. package/rules/feed.yaml +251 -0
  36. package/rules/invitation.yaml +4 -0
  37. package/rules/live-data.yaml +110 -0
  38. package/rules/notification-tray.yaml +4 -0
  39. package/rules/poll.yaml +5 -0
  40. package/rules/sdk-lifecycle.yaml +559 -0
  41. package/rules/search.yaml +5 -0
  42. package/rules/security.yaml +12 -0
  43. package/rules/story.yaml +5 -0
  44. package/rules/user-blocking.yaml +5 -0
  45. package/skills/social-plus-vise/SKILL.md +163 -15
@@ -401,6 +401,143 @@
401
401
  "invalid region"
402
402
  ]
403
403
  },
404
+ {
405
+ "id": "flutter.setup.sync-initialization",
406
+ "version": 1,
407
+ "title": "Flutter setup should complete SDK service initialization before login or live queries",
408
+ "severity": "warning",
409
+ "rationale": "Flutter builds can pass while runtime fails with `MessageDbAdapter not ready` if login or live SDK queries start before the SDK service locator finishes initializing. Use `sycInitialization: true` on AmityCoreClient.setup, or provide equivalent readiness gating before login/query.",
410
+ "applies_when": {
411
+ "platforms": [
412
+ "flutter"
413
+ ],
414
+ "outcomes": [
415
+ "setup-sdk",
416
+ "setup-push",
417
+ "setup-live-data",
418
+ "add-feed",
419
+ "add-chat",
420
+ "validate-setup"
421
+ ]
422
+ },
423
+ "enforcement": {
424
+ "deterministic": [
425
+ {
426
+ "check": "validator-finding-absent",
427
+ "finding_rule_id": "flutter.setup.sync-initialization"
428
+ }
429
+ ],
430
+ "attestation": {
431
+ "allowed": true,
432
+ "host_agent_min_confidence": "high",
433
+ "human_allowed": true,
434
+ "evidence_required": [
435
+ {
436
+ "field": "readiness_gate",
437
+ "description": "Source evidence that setup waits for SDK service initialization or gates login/query until adapters are ready.",
438
+ "upload_policy": "upload-with-consent"
439
+ }
440
+ ]
441
+ }
442
+ },
443
+ "symptoms": [
444
+ "MessageDbAdapter not ready",
445
+ "service locator not ready",
446
+ "blank Flutter splash after setup"
447
+ ]
448
+ },
449
+ {
450
+ "id": "flutter.session.initial-login-kickoff",
451
+ "version": 1,
452
+ "title": "Flutter session observer must also kick off the initial login path",
453
+ "severity": "warning",
454
+ "rationale": "A Flutter screen that only calls login from an observeSessionState listener can stay forever on a signing-in/loading state if the observer does not emit the initial NotLoggedIn state in the captured runtime. Subscribe to session changes, but also seed/kick the current session path immediately after setup/subscription so login or post-login queries run.",
455
+ "applies_when": {
456
+ "platforms": [
457
+ "flutter"
458
+ ],
459
+ "outcomes": [
460
+ "setup-sdk",
461
+ "setup-live-data",
462
+ "add-feed",
463
+ "add-comments",
464
+ "add-chat",
465
+ "add-community",
466
+ "add-follow",
467
+ "validate-setup"
468
+ ]
469
+ },
470
+ "enforcement": {
471
+ "deterministic": [
472
+ {
473
+ "check": "validator-finding-absent",
474
+ "finding_rule_id": "flutter.session.initial-login-kickoff"
475
+ }
476
+ ],
477
+ "attestation": {
478
+ "allowed": true,
479
+ "host_agent_min_confidence": "high",
480
+ "human_allowed": true,
481
+ "evidence_required": [
482
+ {
483
+ "field": "initial_session_kickoff",
484
+ "description": "Source or runtime evidence that login/session-ready work is kicked off immediately as well as on later session-state changes.",
485
+ "upload_policy": "upload-with-consent"
486
+ }
487
+ ]
488
+ }
489
+ },
490
+ "symptoms": [
491
+ "Signing into team circles",
492
+ "Opening the clubhouse",
493
+ "logging in forever",
494
+ "session state never leaves NotLoggedIn"
495
+ ]
496
+ },
497
+ {
498
+ "id": "flutter.dependency.dio-compatible",
499
+ "version": 1,
500
+ "title": "Flutter Amity SDK 7.x must resolve a Dio version below 5.10.0",
501
+ "severity": "warning",
502
+ "rationale": "amity_sdk 7.x allows dio ^5.4.0, so a fresh pub resolution can select dio 5.10.0. dio 5.10.0 adds DioExceptionType.transformTimeout, which makes the SDK's DioExceptionType switch non-exhaustive and fails Android builds even when analyze/test pass.",
503
+ "applies_when": {
504
+ "platforms": [
505
+ "flutter"
506
+ ],
507
+ "outcomes": [
508
+ "setup-sdk",
509
+ "setup-live-data",
510
+ "add-feed",
511
+ "add-community",
512
+ "validate-setup"
513
+ ]
514
+ },
515
+ "enforcement": {
516
+ "deterministic": [
517
+ {
518
+ "check": "validator-finding-absent",
519
+ "finding_rule_id": "flutter.dependency.dio-compatible"
520
+ }
521
+ ],
522
+ "attestation": {
523
+ "allowed": true,
524
+ "host_agent_min_confidence": "high",
525
+ "human_allowed": true,
526
+ "evidence_required": [
527
+ {
528
+ "field": "pubspec_resolution",
529
+ "description": "pubspec.yaml/pubspec.lock evidence that dio resolves below 5.10.0, or SDK release notes proving the selected amity_sdk version no longer has this switch issue.",
530
+ "upload_policy": "upload-with-consent"
531
+ }
532
+ ]
533
+ }
534
+ },
535
+ "symptoms": [
536
+ "DioExceptionType.transformTimeout",
537
+ "The type 'DioExceptionType' is not exhaustively matched",
538
+ "Gradle task assembleDebug failed after resolving dio 5.10.0"
539
+ ]
540
+ },
404
541
  {
405
542
  "id": "flutter.login.present",
406
543
  "version": 1,
@@ -557,6 +694,272 @@
557
694
  }
558
695
  }
559
696
  },
697
+ {
698
+ "id": "react-native.dependency.async-storage",
699
+ "version": 1,
700
+ "title": "React Native SDK must include AsyncStorage native dependency",
701
+ "severity": "warning",
702
+ "rationale": "The React Native social.plus SDK uses AsyncStorage at runtime. React Native autolinking only includes native modules declared by the app, so a TypeScript/Gradle build can pass while the APK crashes with `NativeModule: AsyncStorage is null`.",
703
+ "applies_when": {
704
+ "platforms": [
705
+ "react-native"
706
+ ],
707
+ "outcomes": [
708
+ "setup-sdk",
709
+ "setup-live-data",
710
+ "add-feed",
711
+ "validate-setup"
712
+ ]
713
+ },
714
+ "enforcement": {
715
+ "deterministic": [
716
+ {
717
+ "check": "validator-finding-absent",
718
+ "finding_rule_id": "react-native.dependency.async-storage"
719
+ }
720
+ ],
721
+ "attestation": {
722
+ "allowed": true,
723
+ "host_agent_min_confidence": "high",
724
+ "human_allowed": true,
725
+ "evidence_required": [
726
+ {
727
+ "field": "package_manifest",
728
+ "description": "package.json or package manager catalog showing @react-native-async-storage/async-storage as an app dependency.",
729
+ "upload_policy": "upload-with-consent"
730
+ }
731
+ ]
732
+ }
733
+ }
734
+ },
735
+ {
736
+ "id": "react-native.dependency.async-storage-compatible",
737
+ "version": 1,
738
+ "title": "React Native SDK AsyncStorage dependency must be SDK-compatible",
739
+ "severity": "warning",
740
+ "rationale": "The React Native social.plus SDK 7.x depends on AsyncStorage v1.x. Installing the latest AsyncStorage 2.x/3.x can leave duplicate JS packages in node_modules; Gradle may still autolink a native package, but the SDK's nested JS copy can crash with `NativeModule: AsyncStorage is null`.",
741
+ "applies_when": {
742
+ "platforms": [
743
+ "react-native"
744
+ ],
745
+ "outcomes": [
746
+ "setup-sdk",
747
+ "setup-live-data",
748
+ "add-feed",
749
+ "validate-setup"
750
+ ]
751
+ },
752
+ "enforcement": {
753
+ "deterministic": [
754
+ {
755
+ "check": "validator-finding-absent",
756
+ "finding_rule_id": "react-native.dependency.async-storage-compatible"
757
+ }
758
+ ],
759
+ "attestation": {
760
+ "allowed": true,
761
+ "host_agent_min_confidence": "high",
762
+ "human_allowed": true,
763
+ "evidence_required": [
764
+ {
765
+ "field": "package_manifest",
766
+ "description": "package.json or lockfile showing a single SDK-compatible AsyncStorage v1.x package.",
767
+ "upload_policy": "upload-with-consent"
768
+ }
769
+ ]
770
+ }
771
+ }
772
+ },
773
+ {
774
+ "id": "react-native.dependency.netinfo",
775
+ "version": 1,
776
+ "title": "React Native SDK must include NetInfo native dependency",
777
+ "severity": "warning",
778
+ "rationale": "The React Native social.plus SDK imports @react-native-community/netinfo at runtime. React Native autolinking only includes native modules declared by the app, so TypeScript and Gradle can pass while the APK crashes with `@react-native-community/netinfo: NativeModule.RNCNetInfo is null`.",
779
+ "applies_when": {
780
+ "platforms": [
781
+ "react-native"
782
+ ],
783
+ "outcomes": [
784
+ "setup-sdk",
785
+ "setup-live-data",
786
+ "add-feed",
787
+ "validate-setup"
788
+ ]
789
+ },
790
+ "enforcement": {
791
+ "deterministic": [
792
+ {
793
+ "check": "validator-finding-absent",
794
+ "finding_rule_id": "react-native.dependency.netinfo"
795
+ }
796
+ ],
797
+ "attestation": {
798
+ "allowed": true,
799
+ "host_agent_min_confidence": "high",
800
+ "human_allowed": true,
801
+ "evidence_required": [
802
+ {
803
+ "field": "package_manifest",
804
+ "description": "package.json or package manager catalog showing @react-native-community/netinfo as an app dependency.",
805
+ "upload_policy": "upload-with-consent"
806
+ }
807
+ ]
808
+ }
809
+ }
810
+ },
811
+ {
812
+ "id": "react-native.client.session-state-after-create",
813
+ "version": 1,
814
+ "title": "React Native client session state reads must happen after client creation",
815
+ "severity": "warning",
816
+ "rationale": "React Native can execute module code and React state initializers before the social.plus client is created. Calling Client.isConnected() too early throws `Amity SDK (800000): There is no active client`, which TypeScript and Gradle builds do not catch.",
817
+ "applies_when": {
818
+ "platforms": [
819
+ "react-native"
820
+ ],
821
+ "outcomes": [
822
+ "setup-sdk",
823
+ "setup-live-data",
824
+ "add-feed",
825
+ "validate-setup"
826
+ ]
827
+ },
828
+ "enforcement": {
829
+ "deterministic": [
830
+ {
831
+ "check": "validator-finding-absent",
832
+ "finding_rule_id": "react-native.client.session-state-after-create"
833
+ }
834
+ ],
835
+ "attestation": {
836
+ "allowed": true,
837
+ "host_agent_min_confidence": "high",
838
+ "human_allowed": true,
839
+ "evidence_required": [
840
+ {
841
+ "field": "readiness_guard",
842
+ "description": "Code evidence that Client.isConnected() is called only after createClient has run, or that a safe not-ready state is returned before setup.",
843
+ "upload_policy": "upload-with-consent"
844
+ }
845
+ ]
846
+ }
847
+ }
848
+ },
849
+ {
850
+ "id": "react-native.sdk.no-internal-imports",
851
+ "version": 1,
852
+ "title": "React Native SDK imports must use public package exports",
853
+ "severity": "warning",
854
+ "rationale": "Deep-importing internal social.plus SDK files such as `dist/client/...` can pass TypeScript or Jest while Metro/release bundling fails because those paths are not public package exports.",
855
+ "applies_when": {
856
+ "platforms": [
857
+ "react-native"
858
+ ],
859
+ "outcomes": [
860
+ "setup-sdk",
861
+ "setup-live-data",
862
+ "add-feed",
863
+ "validate-setup"
864
+ ]
865
+ },
866
+ "enforcement": {
867
+ "deterministic": [
868
+ {
869
+ "check": "validator-finding-absent",
870
+ "finding_rule_id": "react-native.sdk.no-internal-imports"
871
+ }
872
+ ],
873
+ "attestation": {
874
+ "allowed": true,
875
+ "host_agent_min_confidence": "high",
876
+ "human_allowed": true,
877
+ "evidence_required": [
878
+ {
879
+ "field": "import_source",
880
+ "description": "Public package-root import that replaces the internal SDK subpath.",
881
+ "upload_policy": "upload-with-consent"
882
+ }
883
+ ]
884
+ }
885
+ }
886
+ },
887
+ {
888
+ "id": "typescript.client.session-state-after-create",
889
+ "version": 1,
890
+ "title": "TypeScript client session state reads must happen after client creation",
891
+ "severity": "warning",
892
+ "rationale": "Client.isConnected() requires an active social.plus client. Reading it from module scope or a startup state initializer before Client.createClient can throw at runtime even when the build passes.",
893
+ "applies_when": {
894
+ "platforms": [
895
+ "typescript"
896
+ ],
897
+ "outcomes": [
898
+ "setup-sdk",
899
+ "setup-live-data",
900
+ "add-feed",
901
+ "validate-setup"
902
+ ]
903
+ },
904
+ "enforcement": {
905
+ "deterministic": [
906
+ {
907
+ "check": "validator-finding-absent",
908
+ "finding_rule_id": "typescript.client.session-state-after-create"
909
+ }
910
+ ],
911
+ "attestation": {
912
+ "allowed": true,
913
+ "host_agent_min_confidence": "high",
914
+ "human_allowed": true,
915
+ "evidence_required": [
916
+ {
917
+ "field": "readiness_guard",
918
+ "description": "Code evidence that Client.isConnected() is called only after createClient has run, or that a safe not-ready state is returned before setup.",
919
+ "upload_policy": "upload-with-consent"
920
+ }
921
+ ]
922
+ }
923
+ }
924
+ },
925
+ {
926
+ "id": "typescript.sdk.no-internal-imports",
927
+ "version": 1,
928
+ "title": "TypeScript SDK imports must use public package exports",
929
+ "severity": "warning",
930
+ "rationale": "Deep-importing internal social.plus SDK files such as `dist/client/...` can pass a narrow type check while production bundlers fail because those paths are not public package exports.",
931
+ "applies_when": {
932
+ "platforms": [
933
+ "typescript"
934
+ ],
935
+ "outcomes": [
936
+ "setup-sdk",
937
+ "setup-live-data",
938
+ "add-feed",
939
+ "validate-setup"
940
+ ]
941
+ },
942
+ "enforcement": {
943
+ "deterministic": [
944
+ {
945
+ "check": "validator-finding-absent",
946
+ "finding_rule_id": "typescript.sdk.no-internal-imports"
947
+ }
948
+ ],
949
+ "attestation": {
950
+ "allowed": true,
951
+ "host_agent_min_confidence": "high",
952
+ "human_allowed": true,
953
+ "evidence_required": [
954
+ {
955
+ "field": "import_source",
956
+ "description": "Public package-root import that replaces the internal SDK subpath.",
957
+ "upload_policy": "upload-with-consent"
958
+ }
959
+ ]
960
+ }
961
+ }
962
+ },
560
963
  {
561
964
  "id": "typescript.client.create",
562
965
  "version": 1,
@@ -1873,6 +2276,162 @@
1873
2276
  ]
1874
2277
  }
1875
2278
  }
2279
+ },
2280
+ {
2281
+ "id": "typescript.sdk.source-used",
2282
+ "version": 1,
2283
+ "title": "TypeScript SDK dependency must be used by product source",
2284
+ "severity": "warning",
2285
+ "rationale": "Installing @amityco/ts-sdk is not an integration. The product source must import/use the SDK or UIKit provider, initialize/login, and query or create social.plus data.",
2286
+ "applies_when": {
2287
+ "platforms": ["typescript"],
2288
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"]
2289
+ },
2290
+ "enforcement": {
2291
+ "deterministic": [
2292
+ { "check": "validator-finding-absent", "finding_rule_id": "typescript.sdk.source-used" }
2293
+ ],
2294
+ "attestation": {
2295
+ "allowed": false,
2296
+ "host_agent_min_confidence": "high",
2297
+ "human_allowed": false,
2298
+ "evidence_required": []
2299
+ }
2300
+ },
2301
+ "symptoms": ["Dependency-only integration", "Static social shell", "No SDK-backed runtime data"]
2302
+ },
2303
+ {
2304
+ "id": "react-native.sdk.source-used",
2305
+ "version": 1,
2306
+ "title": "React Native SDK dependency must be used by product source",
2307
+ "severity": "warning",
2308
+ "rationale": "Installing @amityco/ts-sdk-react-native is not an integration. The product source must import/use the SDK or UIKit provider, initialize/login, and query or create social.plus data.",
2309
+ "applies_when": {
2310
+ "platforms": ["react-native"],
2311
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"]
2312
+ },
2313
+ "enforcement": {
2314
+ "deterministic": [
2315
+ { "check": "validator-finding-absent", "finding_rule_id": "react-native.sdk.source-used" }
2316
+ ],
2317
+ "attestation": {
2318
+ "allowed": false,
2319
+ "host_agent_min_confidence": "high",
2320
+ "human_allowed": false,
2321
+ "evidence_required": []
2322
+ }
2323
+ },
2324
+ "symptoms": ["Dependency-only integration", "Static social shell", "No SDK-backed runtime data"]
2325
+ },
2326
+ {
2327
+ "id": "react-native.ui.runtime-probe-copy",
2328
+ "version": 1,
2329
+ "title": "React Native product UI must not expose runtime probe copy",
2330
+ "severity": "warning",
2331
+ "rationale": "Runtime readiness belongs in evidence and handoff notes, not normal product UI. A visible Runtime/Probe/status panel can make a shallow SDK probe look like a finished integration while users still cannot use the actual social surface.",
2332
+ "applies_when": {
2333
+ "platforms": ["react-native"],
2334
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-community", "add-follow", "validate-setup"]
2335
+ },
2336
+ "enforcement": {
2337
+ "deterministic": [
2338
+ { "check": "validator-finding-absent", "finding_rule_id": "react-native.ui.runtime-probe-copy" }
2339
+ ],
2340
+ "attestation": {
2341
+ "allowed": true,
2342
+ "host_agent_min_confidence": "high",
2343
+ "human_allowed": true,
2344
+ "evidence_required": [
2345
+ {
2346
+ "field": "product_ui_evidence",
2347
+ "description": "Screenshot or source reference showing runtime proof moved to evidence/notes and the visible UI renders normal product social state.",
2348
+ "upload_policy": "upload-with-consent"
2349
+ }
2350
+ ]
2351
+ }
2352
+ },
2353
+ "symptoms": ["Visible Runtime panel", "Probe/status copy in product UI", "SDK readiness labels shown to end users"]
2354
+ },
2355
+ {
2356
+ "id": "react-native.ui.hidden-sdk-noop-control",
2357
+ "version": 1,
2358
+ "title": "React Native SDK usage must not be hidden no-op UI",
2359
+ "severity": "warning",
2360
+ "rationale": "Hidden controls and `void Repository` markers can satisfy naive source scanners without giving users a real reaction/comment/post/report/follow affordance. The visible product UI should call the SDK through meaningful controls.",
2361
+ "applies_when": {
2362
+ "platforms": ["react-native"],
2363
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-community", "add-follow", "validate-setup"]
2364
+ },
2365
+ "enforcement": {
2366
+ "deterministic": [
2367
+ { "check": "validator-finding-absent", "finding_rule_id": "react-native.ui.hidden-sdk-noop-control" }
2368
+ ],
2369
+ "attestation": {
2370
+ "allowed": true,
2371
+ "host_agent_min_confidence": "high",
2372
+ "human_allowed": true,
2373
+ "evidence_required": [
2374
+ {
2375
+ "field": "visible_affordance",
2376
+ "description": "Source or screenshot evidence showing the SDK action is wired to a visible product control.",
2377
+ "upload_policy": "upload-with-consent"
2378
+ }
2379
+ ]
2380
+ }
2381
+ },
2382
+ "symptoms": ["Hidden reaction control", "void Repository marker", "Scanner-only SDK usage"]
2383
+ },
2384
+ {
2385
+ "id": "flutter.sdk.source-used",
2386
+ "version": 1,
2387
+ "title": "Flutter SDK dependency must be used by product source",
2388
+ "severity": "warning",
2389
+ "rationale": "Installing amity_sdk is not an integration. The product source must import/use the SDK or UIKit manager, initialize/login, and query or create social.plus data.",
2390
+ "applies_when": {
2391
+ "platforms": ["flutter"],
2392
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"]
2393
+ },
2394
+ "enforcement": {
2395
+ "deterministic": [
2396
+ { "check": "validator-finding-absent", "finding_rule_id": "flutter.sdk.source-used" }
2397
+ ],
2398
+ "attestation": {
2399
+ "allowed": false,
2400
+ "host_agent_min_confidence": "high",
2401
+ "human_allowed": false,
2402
+ "evidence_required": []
2403
+ }
2404
+ },
2405
+ "symptoms": ["Dependency-only integration", "Static social shell", "No SDK-backed runtime data"]
2406
+ },
2407
+ {
2408
+ "id": "flutter.ui.sdk-data-rendered",
2409
+ "version": 1,
2410
+ "title": "Flutter SDK data must be consumed by visible product UI",
2411
+ "severity": "warning",
2412
+ "rationale": "A Flutter screen can query social.plus data into state but still return the original product screen, making the SDK usage a hidden no-op. Runtime proof requires SDK-returned communities, posts, comments, or users to affect visible product UI or a rendered child widget.",
2413
+ "applies_when": {
2414
+ "platforms": ["flutter"],
2415
+ "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"]
2416
+ },
2417
+ "enforcement": {
2418
+ "deterministic": [
2419
+ { "check": "validator-finding-absent", "finding_rule_id": "flutter.ui.sdk-data-rendered" }
2420
+ ],
2421
+ "attestation": {
2422
+ "allowed": true,
2423
+ "host_agent_min_confidence": "high",
2424
+ "human_allowed": true,
2425
+ "evidence_required": [
2426
+ {
2427
+ "field": "visible_data_binding",
2428
+ "description": "Source or screenshot evidence showing SDK-returned data is rendered in the product UI or passed into a rendered child widget.",
2429
+ "upload_policy": "upload-with-consent"
2430
+ }
2431
+ ]
2432
+ }
2433
+ },
2434
+ "symptoms": ["SDK query result stored but never rendered", "Hidden SDK probe", "Original screen returned after SDK setup"]
1876
2435
  }
1877
2436
  ]
1878
2437
  }
package/rules/search.yaml CHANGED
@@ -5,6 +5,7 @@
5
5
  {
6
6
  "id": "typescript.search.live-collection",
7
7
  "version": 1,
8
+ "symbol_anchored": { "platform": "typescript", "types": ["UserRepository"], "members": ["searchUserByDisplayName"], "basis": "names-only" },
8
9
  "title": "TypeScript search results should use the reactive, paginated Search LiveCollection",
9
10
  "severity": "warning",
10
11
  "rationale": "Search results are paginated Live Collections. A one-shot search (no callback) renders only the first page and never updates. Pass the live callback: searchUserByDisplayName(params, callback) / searchCommunities(params, callback).",
@@ -30,6 +31,7 @@
30
31
  {
31
32
  "id": "react-native.search.live-collection",
32
33
  "version": 1,
34
+ "symbol_anchored": { "platform": "react-native", "types": ["UserRepository"], "members": ["searchUserByDisplayName"], "basis": "names-only" },
33
35
  "title": "React Native search results should use the reactive, paginated Search LiveCollection",
34
36
  "severity": "warning",
35
37
  "rationale": "Search results are paginated Live Collections. A one-shot search (no callback) renders only the first page and never updates. Pass the live callback: searchUserByDisplayName(params, callback) / searchCommunities(params, callback).",
@@ -55,6 +57,7 @@
55
57
  {
56
58
  "id": "android.search.live-collection",
57
59
  "version": 1,
60
+ "symbol_anchored": { "platform": "android", "types": ["AmityUserRepository"], "members": ["searchUsers"], "basis": "names-only" },
58
61
  "title": "Android search results should use the reactive, paginated Search LiveCollection",
59
62
  "severity": "warning",
60
63
  "rationale": "Search results are paginated Live Collections. A one-shot fetch renders only the first page and never updates. Observe the PagingData stream instead: searchUsers(...).query().doOnNext { _: PagingData -> }.subscribe().",
@@ -80,6 +83,7 @@
80
83
  {
81
84
  "id": "ios.search.live-collection",
82
85
  "version": 1,
86
+ "symbol_anchored": { "platform": "ios", "types": ["AmityUserRepository"], "members": ["searchUsers"], "basis": "names-only" },
83
87
  "title": "iOS search results should use the reactive, paginated Search LiveCollection",
84
88
  "severity": "warning",
85
89
  "rationale": "Search results are paginated Live Collections. A one-shot fetch renders only the first page and never updates. Observe the collection instead: searchUsers(...).observe { ... }.",
@@ -105,6 +109,7 @@
105
109
  {
106
110
  "id": "flutter.search.live-collection",
107
111
  "version": 1,
112
+ "symbol_anchored": { "platform": "flutter", "types": ["UserRepository"], "members": ["searchUserByDisplayName"], "basis": "names-only" },
108
113
  "title": "Flutter search results should use the reactive, paginated Search LiveCollection",
109
114
  "severity": "warning",
110
115
  "rationale": "Search results are paginated Live Collections. A one-shot fetch renders only the first page and never updates. Wire a PagingController: PagingController(pageFuture: (token) => searchUserByDisplayName(kw).getPagingData(token: token, limit: 20)).",
@@ -14,6 +14,18 @@
14
14
  "attestation": { "allowed": false, "host_agent_min_confidence": "high", "human_allowed": false, "evidence_required": [] }
15
15
  }
16
16
  },
17
+ {
18
+ "id": "typescript.security.no-unsafe-inner-html",
19
+ "version": 1,
20
+ "title": "TypeScript SDK/user content must not be rendered through raw HTML sinks",
21
+ "severity": "warning",
22
+ "rationale": "SDK and user-authored text can contain attacker-controlled markup. Rendering it through innerHTML, insertAdjacentHTML, or dangerouslySetInnerHTML creates an XSS surface; use text nodes or sanitize first.",
23
+ "applies_when": { "platforms": ["typescript"], "outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"] },
24
+ "enforcement": {
25
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "typescript.security.no-unsafe-inner-html" }],
26
+ "attestation": { "allowed": false, "host_agent_min_confidence": "high", "human_allowed": false, "evidence_required": [] }
27
+ }
28
+ },
17
29
  {
18
30
  "id": "react-native.secret.inline-api-key",
19
31
  "version": 1,