@amityco/social-plus-vise 0.14.19 → 0.14.21
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.
- package/CHANGELOG.md +21 -0
- package/README.md +3 -3
- package/dist/capabilities.js +316 -0
- package/dist/outcomes.js +74 -17
- package/dist/productExpectations.js +42 -0
- package/package.json +1 -1
- package/rules/feed.yaml +10 -5
- package/rules/moderation.yaml +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ All notable changes to `@amityco/social-plus-vise` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.14.21 — 2026-06-05
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **Core surface product expectations:** feed, comment tray, chat inbox, and profile plans now surface shared product expectations for post-type scope, composer type scope, live comment lists, comment creation, comment UI states, live channel lists, channel-list sorting, profile identity, and profile avatars.
|
|
11
|
+
- **Plan-time scope questions:** add-feed plans now ask for feed post-type and composer scope; add-comments asks for comment tray scope; add-chat asks for inbox unread/sorting scope; add-follow asks for profile identity scope.
|
|
12
|
+
- **Broad social routing clarity:** `feature_surface` now lists `comments` as a first-class option alongside feed, profile, community, chat, and notifications.
|
|
13
|
+
|
|
14
|
+
### Verified
|
|
15
|
+
- Targeted product-flow and capability coverage now locks the feed-forward path for the new shared expectations across TypeScript, React Native, Android, Flutter, and iOS SDK surfaces.
|
|
16
|
+
|
|
17
|
+
## 0.14.20 — 2026-06-05
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Product-first community/follow/notification flow:** community, follow/social-graph, and in-app notification plans now surface shared product expectation IDs before implementation, backed by platform capability preflight.
|
|
21
|
+
- **Community deterministic bindings:** community avatar, community display-name, and role-gated moderator action findings now report shared IDs (`community.avatar-from-sdk`, `community.display-name-from-sdk`, `moderation.role-gated-action`) while retaining exact platform `contractRuleId`/`validator.sensorId` evidence.
|
|
22
|
+
- **Attestation fallback for non-deterministic product expectations:** community target/live/privacy, follow target/live/model, and notification tray/seen/preferences expectations are offered when bundled SDK facts show the surface exists, with `attestation-needed` status until dedicated deterministic sensors exist.
|
|
23
|
+
- **Availability-aware notification guidance:** `vise plan` now withholds unavailable notification tray/mark-seen capabilities from Flutter while still surfacing notification preferences, and withholds notification preferences from TypeScript/React Native where bundled facts do not expose notification settings APIs.
|
|
24
|
+
|
|
25
|
+
### Verified
|
|
26
|
+
- Full `npm run validate` passed, including package E2E and pack dry-run for `@amityco/social-plus-vise@0.14.20`. Product-flow now verifies community/follow/notification plan feed-forward, unavailable notification capability reporting, and exact Android sensor evidence for promoted community/moderation expectations.
|
|
27
|
+
|
|
7
28
|
## 0.14.19 — 2026-06-05
|
|
8
29
|
|
|
9
30
|
### Changed
|
package/README.md
CHANGED
|
@@ -161,14 +161,14 @@ Aggregate: **98/99 expected feed capabilities** and **27/27 selected optional ca
|
|
|
161
161
|
|
|
162
162
|
### Current Release Validation
|
|
163
163
|
|
|
164
|
-
Version 0.14.
|
|
164
|
+
Version 0.14.21 carries current release proof around the full feed-forward, product-expectation, and validation flow:
|
|
165
165
|
|
|
166
166
|
| Surface | What was validated |
|
|
167
167
|
|---|---|
|
|
168
168
|
| **Product flow** | Local end-to-end smoke covers design extraction, plan feed-forward, blocking intake, answered init, capability check, design conformance, and sensor discovery. |
|
|
169
|
-
| **Plan questions** | Plans surface blocking questions such as `feature_surface` and `design_contract_confirmation`, plus optional choices such as `feed_optional_capabilities`. |
|
|
169
|
+
| **Plan questions** | Plans surface blocking questions such as `feature_surface` and `design_contract_confirmation`, product-scope questions such as `feed_post_type_scope`, `feed_composer_type_scope`, `comment_tray_scope`, `chat_inbox_scope`, and `profile_identity_scope`, plus optional choices such as `feed_optional_capabilities`. |
|
|
170
170
|
| **Capability-to-sensor flow** | Vise checks platform support, matches the prompt to available capabilities, offers supported features as questions, records answers, and turns selected answers into sensors in `vise check`. |
|
|
171
|
-
| **Shared product expectations** | Public IDs such as `feed.target-resolved`, `
|
|
171
|
+
| **Shared product expectations** | Public IDs such as `feed.target-resolved`, `feed.post-type-scope-explicit`, `comments.creation-affordance`, `chat.channel-list-order-explicit`, `community.avatar-from-sdk`, `moderation.role-gated-action`, `follow.relationship-live`, `profile.identity-from-sdk`, `profile.social-counts`, and `notifications.tray-live` stay platform-agnostic while check results retain concrete `contractRuleId` and `validator.sensorId` evidence when deterministic sensors exist. |
|
|
172
172
|
| **Rule detection** | TP-track dashboard detects **321/321 seeded rule gaps (100.0%)** in the static corpus. |
|
|
173
173
|
| **Packed-package smoke** | Packed-package and host-agent smokes exercise the release tarball path, surfaced plan questions, selected optional capability sensors, rejected design confirmation handling, and exact contract-rule evidence for shared product expectations. |
|
|
174
174
|
|
package/dist/capabilities.js
CHANGED
|
@@ -569,6 +569,50 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
569
569
|
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
570
570
|
hint: "implement the selected rich composer paths or record an explicit text-only/rich-post scope decision",
|
|
571
571
|
},
|
|
572
|
+
{
|
|
573
|
+
id: "feed.post-type-scope-explicit",
|
|
574
|
+
label: "Feed post-type scope",
|
|
575
|
+
outcomes: ["add-feed"],
|
|
576
|
+
kind: "shared-expectation",
|
|
577
|
+
availability: [
|
|
578
|
+
{
|
|
579
|
+
label: "SDK rich post models/accessors",
|
|
580
|
+
symbols: [
|
|
581
|
+
/\b(?:ContentDataImage|ImageData|AmityImagePost\w*|ImagePost\w*)\b/i,
|
|
582
|
+
/\b(?:ContentDataVideo|VideoData|AmityVideoPost\w*|VideoPost\w*)\b/i,
|
|
583
|
+
/\b(?:ContentDataFile|FileData|AmityFilePost\w*|FilePost\w*)\b/i,
|
|
584
|
+
/\b(?:ContentDataPoll|PollData|AmityPollPost\w*|PollPost\w*)\b/i,
|
|
585
|
+
/\b(?:ContentDataClip|AmityClipPost\w*|ClipPost\w*)\b/i,
|
|
586
|
+
/\b(?:ContentDataRoom|LiveStreamData|AmityRoomPost\w*|RoomPost\w*|LiveStreamPost\w*)\b/i,
|
|
587
|
+
],
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
deterministicPlatforms: [],
|
|
591
|
+
hint: "before building, name the SDK-available post types the feed will render: image, video, file, poll, clip, room/livestream, custom, or an explicit text-only scope",
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
id: "feed.composer-type-scope-explicit",
|
|
595
|
+
label: "Post composer type scope",
|
|
596
|
+
outcomes: ["add-feed"],
|
|
597
|
+
kind: "shared-expectation",
|
|
598
|
+
availability: [
|
|
599
|
+
{
|
|
600
|
+
label: "SDK rich post creation",
|
|
601
|
+
symbols: [
|
|
602
|
+
/\bcreateImagePost\b/i,
|
|
603
|
+
/\bcreateVideoPost\b/i,
|
|
604
|
+
/\bcreateFilePost\b/i,
|
|
605
|
+
/\bcreatePollPost\b/i,
|
|
606
|
+
/\bcreateClipPost\b/i,
|
|
607
|
+
/\bcreateRoomPost\b/i,
|
|
608
|
+
/\bcreateMixedAttachmentPost\b/i,
|
|
609
|
+
/\bcreatePost\b/i,
|
|
610
|
+
],
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
deterministicPlatforms: [],
|
|
614
|
+
hint: "before building a composer, name which SDK-available creation paths are in scope and which are intentionally excluded",
|
|
615
|
+
},
|
|
572
616
|
{
|
|
573
617
|
id: "comments.target-resolved",
|
|
574
618
|
label: "Resolved comment target",
|
|
@@ -595,6 +639,48 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
595
639
|
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
596
640
|
hint: "if comments are shown, pair the list with a composer and loading/error/empty states unless the surface is explicitly read-only",
|
|
597
641
|
},
|
|
642
|
+
{
|
|
643
|
+
id: "comments.list-live",
|
|
644
|
+
label: "Live comment list",
|
|
645
|
+
outcomes: ["add-feed", "add-comments"],
|
|
646
|
+
kind: "shared-expectation",
|
|
647
|
+
availability: [
|
|
648
|
+
{
|
|
649
|
+
label: "SDK comment query/live APIs",
|
|
650
|
+
symbols: [/\bgetComments\b/i, /\bqueryComments\b/i, /\bCommentRepository\b/i, /\bLiveCollection\b/i, /\bPagingData\b/i],
|
|
651
|
+
},
|
|
652
|
+
],
|
|
653
|
+
deterministicPlatforms: [],
|
|
654
|
+
hint: "render a live comment list from the parent entity, not a static empty tray or placeholder",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
id: "comments.creation-affordance",
|
|
658
|
+
label: "Comment creation affordance",
|
|
659
|
+
outcomes: ["add-feed", "add-comments"],
|
|
660
|
+
kind: "shared-expectation",
|
|
661
|
+
availability: [
|
|
662
|
+
{
|
|
663
|
+
label: "SDK comment creation",
|
|
664
|
+
symbols: [/\bcreateComment\b/i, /\bCommentCreate\b/i, /\bAmityCommentCreateOptions\b/i],
|
|
665
|
+
},
|
|
666
|
+
],
|
|
667
|
+
deterministicPlatforms: [],
|
|
668
|
+
hint: "when comments are in scope, include a composer that calls the SDK createComment path, or explicitly record read-only scope",
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
id: "comments.ui-states-present",
|
|
672
|
+
label: "Comment loading/empty/error states",
|
|
673
|
+
outcomes: ["add-feed", "add-comments"],
|
|
674
|
+
kind: "shared-expectation",
|
|
675
|
+
availability: [
|
|
676
|
+
{
|
|
677
|
+
label: "SDK comment query/live APIs",
|
|
678
|
+
symbols: [/\bgetComments\b/i, /\bqueryComments\b/i, /\bCommentRepository\b/i, /\bLiveCollection\b/i, /\bPagingData\b/i],
|
|
679
|
+
},
|
|
680
|
+
],
|
|
681
|
+
deterministicPlatforms: [],
|
|
682
|
+
hint: "comment trays should distinguish loading, empty, error, and data states before showing an empty list",
|
|
683
|
+
},
|
|
598
684
|
{
|
|
599
685
|
id: "chat.channel-target-resolved",
|
|
600
686
|
label: "Resolved chat channel",
|
|
@@ -651,6 +737,34 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
651
737
|
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
652
738
|
hint: "match the SDK channel type to the selected chat shape: conversation for direct messages, community/group for shared channels, broadcast where applicable",
|
|
653
739
|
},
|
|
740
|
+
{
|
|
741
|
+
id: "chat.channel-list-live",
|
|
742
|
+
label: "Live chat channel list",
|
|
743
|
+
outcomes: ["add-chat"],
|
|
744
|
+
kind: "shared-expectation",
|
|
745
|
+
availability: [
|
|
746
|
+
{
|
|
747
|
+
label: "SDK channel query/live APIs",
|
|
748
|
+
symbols: [/\bChannelRepository\b/i, /\bgetChannels\b/i, /\bqueryChannels\b/i, /\bAmityChannel\b/i, /\bLiveCollection\b/i, /\bPagingData\b/i],
|
|
749
|
+
},
|
|
750
|
+
],
|
|
751
|
+
deterministicPlatforms: [],
|
|
752
|
+
hint: "build the inbox from SDK channel queries or live collections, not static rows or a fixed channel",
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
id: "chat.channel-list-order-explicit",
|
|
756
|
+
label: "Explicit chat channel-list order",
|
|
757
|
+
outcomes: ["add-chat"],
|
|
758
|
+
kind: "shared-expectation",
|
|
759
|
+
availability: [
|
|
760
|
+
{
|
|
761
|
+
label: "SDK channel sort/order",
|
|
762
|
+
symbols: [/\bChannelSort\b/i, /\bAmityChannelSortOption\b/i, /\bsortByLastActivity\b/i, /\bsortBy\b/i, /\blastActivity\b/i, /\bsortingOrder\b/i],
|
|
763
|
+
},
|
|
764
|
+
],
|
|
765
|
+
deterministicPlatforms: [],
|
|
766
|
+
hint: "declare inbox/channel-list sorting, usually last activity descending, so the UI cannot reverse recency by default",
|
|
767
|
+
},
|
|
654
768
|
{
|
|
655
769
|
id: "chat.unread-visible",
|
|
656
770
|
label: "Chat unread counts",
|
|
@@ -688,6 +802,160 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
688
802
|
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
689
803
|
hint: "declare first-created/newest-created order in the SDK query or a clearly named UI sort so the thread cannot be reversed by defaults",
|
|
690
804
|
},
|
|
805
|
+
{
|
|
806
|
+
id: "community.target-resolved",
|
|
807
|
+
label: "Resolved community target",
|
|
808
|
+
outcomes: ["add-community"],
|
|
809
|
+
kind: "shared-expectation",
|
|
810
|
+
availability: [
|
|
811
|
+
{
|
|
812
|
+
label: "SDK community query/target APIs",
|
|
813
|
+
symbols: [/\bcommunityId\b/i, /\bCommunityRepository\b/i, /\bgetCommunity\b/i, /\bqueryCommunities\b/i, /\bgetCommunities\b/i, /\bAmityCommunity\b/i],
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
deterministicPlatforms: [],
|
|
817
|
+
hint: "resolve communityId from route params, user selection, SDK query, or a create flow; do not invent or hardcode it",
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
id: "community.members-live",
|
|
821
|
+
label: "Live community/member lists",
|
|
822
|
+
outcomes: ["add-community"],
|
|
823
|
+
kind: "shared-expectation",
|
|
824
|
+
availability: [
|
|
825
|
+
{
|
|
826
|
+
label: "SDK community/member live APIs",
|
|
827
|
+
symbols: [/\bCommunityLiveCollection\b/i, /\bCommunityMemberLiveCollection\b/i, /\bgetMembers\b/i, /\bsearchMembers\b/i, /\bgetLiveCollection\b/i, /\blisten\b/i, /\bobserve\b/i],
|
|
828
|
+
},
|
|
829
|
+
],
|
|
830
|
+
deterministicPlatforms: [],
|
|
831
|
+
hint: "query communities and members through the SDK live collection/listener idiom, with lifecycle cleanup",
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
id: "community.privacy-flow-explicit",
|
|
835
|
+
label: "Community privacy/join flow",
|
|
836
|
+
outcomes: ["add-community"],
|
|
837
|
+
kind: "shared-expectation",
|
|
838
|
+
availability: [
|
|
839
|
+
{
|
|
840
|
+
label: "SDK join/leave and join-request APIs",
|
|
841
|
+
symbols: [/\bjoinCommunity\b/i, /\bleaveCommunity\b/i, /\bjoinRequest\b/i, /\bJoinRequest\b/i, /\bCommunityType\b/i, /\bMembershipAcceptanceType\b/i],
|
|
842
|
+
},
|
|
843
|
+
],
|
|
844
|
+
deterministicPlatforms: [],
|
|
845
|
+
hint: "handle public instant-join and private join-request states explicitly instead of assuming one community privacy model",
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
id: "community.avatar-from-sdk",
|
|
849
|
+
label: "Community avatar from SDK",
|
|
850
|
+
outcomes: ["add-feed", "add-comments", "add-community"],
|
|
851
|
+
kind: "shared-expectation",
|
|
852
|
+
availability: [
|
|
853
|
+
{
|
|
854
|
+
label: "SDK avatar fields/accessors",
|
|
855
|
+
symbols: [/\bAmityCommunity\b/i, /\bAmity\.Community\b/i, /\bavatar\b/i, /\bavatarImage\b/i, /\bgetAvatar\b/i, /\bfileUrl\b/i, /\bfileURL\b/i],
|
|
856
|
+
},
|
|
857
|
+
],
|
|
858
|
+
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
859
|
+
hint: "render the SDK avatar URL when present and fall back to initials only when no SDK avatar exists",
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
id: "community.display-name-from-sdk",
|
|
863
|
+
label: "Community display name from SDK",
|
|
864
|
+
outcomes: ["add-feed", "add-comments", "add-community"],
|
|
865
|
+
kind: "shared-expectation",
|
|
866
|
+
availability: [
|
|
867
|
+
{
|
|
868
|
+
label: "SDK community display name",
|
|
869
|
+
symbols: [/\bdisplayName\b/i, /\bgetDisplayName\b/i],
|
|
870
|
+
},
|
|
871
|
+
],
|
|
872
|
+
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
873
|
+
hint: "render community.displayName or the platform equivalent; do not show raw community IDs as names",
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
id: "moderation.role-gated-action",
|
|
877
|
+
label: "Role-gated moderator actions",
|
|
878
|
+
outcomes: ["add-community", "add-moderation"],
|
|
879
|
+
kind: "shared-expectation",
|
|
880
|
+
availability: [
|
|
881
|
+
{
|
|
882
|
+
label: "SDK role or member-moderation APIs",
|
|
883
|
+
symbols: [/\baddRole\b/i, /\bremoveRole\b/i, /\bbanMember\b/i, /\bremoveMember\b/i, /\broles?\b/i, /\bpermissions\b/i, /\bmoderation\b/i],
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
887
|
+
hint: "gate moderator-only role, ban, remove, mute, approve, or decline actions behind SDK role/permission state",
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
id: "follow.target-resolved",
|
|
891
|
+
label: "Resolved follow target",
|
|
892
|
+
outcomes: ["add-follow"],
|
|
893
|
+
kind: "shared-expectation",
|
|
894
|
+
availability: [
|
|
895
|
+
{
|
|
896
|
+
label: "SDK user relationship target APIs",
|
|
897
|
+
symbols: [/\buserId\b/i, /\btargetUserId\b/i, /\bUserRepository\b/i, /\bfollow\b/i, /\bunfollow\b/i, /\bgetFollowInfo\b/i],
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
deterministicPlatforms: [],
|
|
901
|
+
hint: "resolve the target userId from route params, selected profile, or current user context; do not invent it",
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
id: "follow.relationship-live",
|
|
905
|
+
label: "Live follow relationship",
|
|
906
|
+
outcomes: ["add-follow"],
|
|
907
|
+
kind: "shared-expectation",
|
|
908
|
+
availability: [
|
|
909
|
+
{
|
|
910
|
+
label: "SDK follower/following live APIs",
|
|
911
|
+
symbols: [/\bFollowerLiveCollection\b/i, /\bFollowingLiveCollection\b/i, /\bgetFollowers\b/i, /\bgetFollowings\b/i, /\bgetLiveCollection\b/i, /\blisten\b/i, /\bonFollow/i],
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
deterministicPlatforms: [],
|
|
915
|
+
hint: "keep follow state and follower/following lists live through SDK subscriptions or live collections, with cleanup",
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
id: "follow.model-explicit",
|
|
919
|
+
label: "Follow approval model",
|
|
920
|
+
outcomes: ["add-follow"],
|
|
921
|
+
kind: "shared-expectation",
|
|
922
|
+
availability: [
|
|
923
|
+
{
|
|
924
|
+
label: "SDK follow status/request APIs",
|
|
925
|
+
symbols: [/\bFollowStatus\b/i, /\bFollowStatusFilter\b/i, /\bpending\b/i, /\bacceptMyFollower\b/i, /\bdeclineMyFollower\b/i],
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
deterministicPlatforms: [],
|
|
929
|
+
hint: "make automatic follow vs pending approval explicit, including accept/decline states where the SDK exposes them",
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
id: "profile.identity-from-sdk",
|
|
933
|
+
label: "Profile identity from SDK",
|
|
934
|
+
outcomes: ["add-follow"],
|
|
935
|
+
kind: "shared-expectation",
|
|
936
|
+
availability: [
|
|
937
|
+
{
|
|
938
|
+
label: "SDK user identity APIs",
|
|
939
|
+
symbols: [/\bUserRepository\b/i, /\bgetUser\b/i, /\bgetUsers\b/i, /\bAmityUser\b/i, /\bdisplayName\b/i, /\buserId\b/i],
|
|
940
|
+
},
|
|
941
|
+
],
|
|
942
|
+
deterministicPlatforms: [],
|
|
943
|
+
hint: "source profile name, id, avatar, and relationship state from the SDK user/profile object instead of placeholders",
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
id: "profile.avatar-from-sdk",
|
|
947
|
+
label: "Profile avatar from SDK",
|
|
948
|
+
outcomes: ["add-follow"],
|
|
949
|
+
kind: "shared-expectation",
|
|
950
|
+
availability: [
|
|
951
|
+
{
|
|
952
|
+
label: "SDK user avatar fields/accessors",
|
|
953
|
+
symbols: [/\bavatar\b/i, /\bavatarImage\b/i, /\bavatarUrl\b/i, /\bavatarFileId\b/i, /\bgetAvatar\b/i, /\bfileUrl\b/i, /\bfileURL\b/i],
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
deterministicPlatforms: [],
|
|
957
|
+
hint: "render the SDK user avatar when present and fall back to initials only when the SDK has no avatar field",
|
|
958
|
+
},
|
|
691
959
|
{
|
|
692
960
|
id: "profile.social-counts",
|
|
693
961
|
label: "Profile follower/following counts",
|
|
@@ -709,6 +977,54 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
709
977
|
deterministicPlatforms: ["android", "flutter", "ios", "typescript"],
|
|
710
978
|
hint: "if follower/following labels are rendered, source the counts or lists from the SDK instead of placeholders",
|
|
711
979
|
},
|
|
980
|
+
{
|
|
981
|
+
id: "notifications.tray-live",
|
|
982
|
+
label: "Live notification tray",
|
|
983
|
+
outcomes: ["add-notifications"],
|
|
984
|
+
kind: "shared-expectation",
|
|
985
|
+
availability: [
|
|
986
|
+
{
|
|
987
|
+
label: "SDK notification tray APIs",
|
|
988
|
+
symbols: [/\bnotificationTray\b/i, /\bNotificationTray\b/i, /\bgetNotificationTrayItems\b/i, /\bgetNotificationTraySeen\b/i],
|
|
989
|
+
},
|
|
990
|
+
],
|
|
991
|
+
deterministicPlatforms: [],
|
|
992
|
+
hint: "observe the in-app notification tray/list or seen object through the SDK; do not treat it as push setup",
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
id: "notifications.mark-seen",
|
|
996
|
+
label: "Notification seen state",
|
|
997
|
+
outcomes: ["add-notifications"],
|
|
998
|
+
kind: "shared-expectation",
|
|
999
|
+
availability: [
|
|
1000
|
+
{
|
|
1001
|
+
label: "SDK tray seen/mark-seen APIs",
|
|
1002
|
+
symbols: [/\bmarkTraySeen\b/i, /\bmarkItemsSeen\b/i, /\bmarkSeen\b/i, /\bNotificationTraySeen\b/i],
|
|
1003
|
+
},
|
|
1004
|
+
],
|
|
1005
|
+
deterministicPlatforms: [],
|
|
1006
|
+
hint: "call the SDK mark-seen API so tray badges clear server-side after the user opens the notification surface",
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
id: "notifications.preferences-respected",
|
|
1010
|
+
label: "Notification preferences",
|
|
1011
|
+
outcomes: ["add-notifications"],
|
|
1012
|
+
kind: "shared-expectation",
|
|
1013
|
+
availability: [
|
|
1014
|
+
{
|
|
1015
|
+
label: "SDK notification settings/preferences APIs",
|
|
1016
|
+
symbols: [
|
|
1017
|
+
/\bAmity(?:Channel|Community|User)Notifications?Manager\.getSettings\b/i,
|
|
1018
|
+
/\bAmity(?:Channel|Community|User)Notification\.getSettings\b/i,
|
|
1019
|
+
/\bAmitySocialClient\.getSettings\b/i,
|
|
1020
|
+
/\bnotificationRepository\.getSettings\b/i,
|
|
1021
|
+
/\bnotifications\(\)\.getSettings\b/i,
|
|
1022
|
+
],
|
|
1023
|
+
},
|
|
1024
|
+
],
|
|
1025
|
+
deterministicPlatforms: [],
|
|
1026
|
+
hint: "when settings are in scope, read or respect SDK notification preferences rather than showing hardcoded toggles",
|
|
1027
|
+
},
|
|
712
1028
|
];
|
|
713
1029
|
function availabilityDefinitionsFor(outcome) {
|
|
714
1030
|
return [
|
package/dist/outcomes.js
CHANGED
|
@@ -492,7 +492,7 @@ const addFeed = {
|
|
|
492
492
|
why: "The request is broad, so the coding agent needs one product surface before it can choose SDK docs, UI states, and target files.",
|
|
493
493
|
required: true,
|
|
494
494
|
blocksImplementationWhenMissing: true,
|
|
495
|
-
options: ["feed", "profile", "community", "chat", "notifications"],
|
|
495
|
+
options: ["feed", "comments", "profile", "community", "chat", "notifications"],
|
|
496
496
|
});
|
|
497
497
|
}
|
|
498
498
|
questions.push({
|
|
@@ -526,12 +526,28 @@ const addFeed = {
|
|
|
526
526
|
blocksImplementationWhenMissing: false,
|
|
527
527
|
options: ["comments + replies", "reactions", "notifications", "sharing", "feed only (no engagement)"],
|
|
528
528
|
});
|
|
529
|
+
questions.push({
|
|
530
|
+
id: "feed_post_type_scope",
|
|
531
|
+
question: "Which SDK-available post types should the feed render?",
|
|
532
|
+
why: "A feed that renders only text can look complete in a smoke test while image, video, poll, clip, room, and custom posts are blank. The agent should either render the available post types or record an explicit text-only/product subset decision.",
|
|
533
|
+
required: false,
|
|
534
|
+
blocksImplementationWhenMissing: false,
|
|
535
|
+
options: ["all SDK-available post types", "text only", "text + image + video", "text + image + video + poll", "custom subset"],
|
|
536
|
+
});
|
|
537
|
+
questions.push({
|
|
538
|
+
id: "feed_composer_type_scope",
|
|
539
|
+
question: "Which SDK-available post creation types should the composer expose?",
|
|
540
|
+
why: "Post creation often ships as text-only while the feed can render richer content. The agent should align composer scope with the product decision and SDK availability.",
|
|
541
|
+
required: false,
|
|
542
|
+
blocksImplementationWhenMissing: false,
|
|
543
|
+
options: ["all SDK-available creation types", "text only", "text + media", "text + media + poll", "custom subset"],
|
|
544
|
+
});
|
|
529
545
|
return filterAnswered(ctx.answers, questions);
|
|
530
546
|
},
|
|
531
547
|
requiredInputs: () => [
|
|
532
548
|
"live data shape: single object or collection",
|
|
533
549
|
"lifecycle owner for subscribe/unsubscribe cleanup",
|
|
534
|
-
"feature choice: feed, profile, community, chat, notifications, or another social surface",
|
|
550
|
+
"feature choice: feed, comments, profile, community, chat, notifications, or another social surface",
|
|
535
551
|
"feed scope: global, user, or community",
|
|
536
552
|
"concrete feed target for reads and post creation: existing communityId, selected user target, current user target, or app-defined feed target",
|
|
537
553
|
"target screen or route for the feed UI",
|
|
@@ -586,6 +602,7 @@ const addFeed = {
|
|
|
586
602
|
evidence: [
|
|
587
603
|
"capabilityAvailability.available",
|
|
588
604
|
"optionalCapabilities.choices",
|
|
605
|
+
"intake.feed_composer_type_scope",
|
|
589
606
|
"social-plus-sdk/social/content-management/posts/creation/image-post",
|
|
590
607
|
"social-plus-sdk/core-concepts/file-handling/upload",
|
|
591
608
|
],
|
|
@@ -594,7 +611,7 @@ const addFeed = {
|
|
|
594
611
|
{ step: "Implement loading, empty, error, and data states.", evidence: ["implementationRules.file-specific edits"] },
|
|
595
612
|
{
|
|
596
613
|
step: "In post card renderers, resolve each media type from the parent post OR its childrenPosts — in a feed the parent is usually dataType 'text' and the image/video/poll/clip/room rides on a child post. Handle at minimum: text, image, video, file, poll, clip, room. Do not render placeholder labels like '[Image post]' or '[Poll post]'; read the SDK data/accessors and render the actual content. Do NOT gate media on the parent's dataType alone (e.g. post.dataType === 'poll') — that never matches a text parent and the content silently never renders. When rendering the text body itself (post or comment), apply @mention highlights if metadata carries mention entries ({ type: 'user', index, length, userId }, length excluding the '@'): wrap each [index, index + length + 1] span in a styled element and resolve userId to a display name, rather than printing raw text. Pass mentionees on create so mentioned users are notified.",
|
|
597
|
-
evidence: ["social-plus-sdk/social/posts"],
|
|
614
|
+
evidence: ["social-plus-sdk/social/posts", "intake.feed_post_type_scope", "capabilityAvailability.available"],
|
|
598
615
|
},
|
|
599
616
|
{
|
|
600
617
|
step: "Read SDK objects through their own typed accessors and types (e.g. post.getImageInfo(), the Amity.* types) instead of casting return values to hand-written shapes like (post.data as { text?: string }). A hand-written cast silences the type-checker, so when an SDK upgrade renames a field your build still compiles and the bug only surfaces at runtime — reading through SDK types lets tsc/analyze flag the breakage.",
|
|
@@ -662,6 +679,11 @@ const addFeed = {
|
|
|
662
679
|
"unread.server-synced",
|
|
663
680
|
"feed.rich-post-rendering",
|
|
664
681
|
"feed.rich-post-composer-scope",
|
|
682
|
+
"feed.post-type-scope-explicit",
|
|
683
|
+
"feed.composer-type-scope-explicit",
|
|
684
|
+
"comments.list-live",
|
|
685
|
+
"comments.creation-affordance",
|
|
686
|
+
"comments.ui-states-present",
|
|
665
687
|
"comments.thread-read-write",
|
|
666
688
|
"profile.social-counts",
|
|
667
689
|
],
|
|
@@ -750,6 +772,14 @@ const addComments = {
|
|
|
750
772
|
required: true,
|
|
751
773
|
blocksImplementationWhenMissing: true,
|
|
752
774
|
},
|
|
775
|
+
{
|
|
776
|
+
id: "comment_tray_scope",
|
|
777
|
+
question: "Should the comment tray include the live list, composer, and loading/error/empty states?",
|
|
778
|
+
why: "A tray that only opens, only shows placeholders, or only reads comments misses the expected comment workflow. If any part is intentionally omitted, the agent should record that product decision.",
|
|
779
|
+
required: false,
|
|
780
|
+
blocksImplementationWhenMissing: false,
|
|
781
|
+
options: ["list + composer + states", "read-only list", "composer elsewhere", "custom subset"],
|
|
782
|
+
},
|
|
753
783
|
];
|
|
754
784
|
return filterAnswered(ctx.answers, questions);
|
|
755
785
|
},
|
|
@@ -784,8 +814,8 @@ const addComments = {
|
|
|
784
814
|
evidence: [commentDocPath(ctx.platform), liveDataPlatformPath(ctx.platform)],
|
|
785
815
|
},
|
|
786
816
|
{ step: "Reuse the host app's existing visual system for the comment UI.", evidence: designEvidence },
|
|
787
|
-
{ step: "Implement loading, empty, error, and data states
|
|
788
|
-
{ step: "Add comment creation (composer) with auth gate and error handling.", evidence: ["requiredInputs.concrete parent entity ID"] },
|
|
817
|
+
{ step: "Implement the live comment list plus loading, empty, error, and data states before showing an empty tray.", evidence: ["implementationRules.file-specific edits", "intake.comment_tray_scope"] },
|
|
818
|
+
{ step: "Add comment creation (composer) with auth gate and error handling unless the product explicitly chose a read-only tray.", evidence: ["requiredInputs.concrete parent entity ID", "intake.comment_tray_scope"] },
|
|
789
819
|
{ step: "Wire moderation affordance (report/hide/delete) on each comment.", evidence: ["implementationRules.file-specific edits"] },
|
|
790
820
|
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
791
821
|
];
|
|
@@ -794,6 +824,9 @@ const addComments = {
|
|
|
794
824
|
"comment target resolved",
|
|
795
825
|
"no invented postId/commentId",
|
|
796
826
|
"comments.target-resolved",
|
|
827
|
+
"comments.list-live",
|
|
828
|
+
"comments.creation-affordance",
|
|
829
|
+
"comments.ui-states-present",
|
|
797
830
|
"comments.thread-read-write",
|
|
798
831
|
"moderation.affordance-present",
|
|
799
832
|
"pagination.cursor-opaque",
|
|
@@ -903,6 +936,7 @@ const addModeration = {
|
|
|
903
936
|
`${platform}.moderation.block-or-mute-state-applied`,
|
|
904
937
|
`${platform}.moderation.hidden-content-rendering-present`,
|
|
905
938
|
`${platform}.moderation.confirmation-ux-present`,
|
|
939
|
+
"moderation.role-gated-action",
|
|
906
940
|
],
|
|
907
941
|
stopConditions: (ctx) => filterStops(ctx.answers, [
|
|
908
942
|
{ id: "moderation_target", text: "The moderation target content types are unknown." },
|
|
@@ -987,6 +1021,14 @@ const addChat = {
|
|
|
987
1021
|
required: true,
|
|
988
1022
|
blocksImplementationWhenMissing: false,
|
|
989
1023
|
},
|
|
1024
|
+
{
|
|
1025
|
+
id: "chat_inbox_scope",
|
|
1026
|
+
question: "Should the chat inbox show live channels with unread badges and explicit recency sorting?",
|
|
1027
|
+
why: "A message thread can work while the inbox silently drops unread state or reverses channel recency. The agent should confirm the channel-list behavior before building.",
|
|
1028
|
+
required: false,
|
|
1029
|
+
blocksImplementationWhenMissing: false,
|
|
1030
|
+
options: ["live channels + unread + last activity sort", "thread only", "no unread badges", "custom sort"],
|
|
1031
|
+
},
|
|
990
1032
|
];
|
|
991
1033
|
return filterAnswered(ctx.answers, questions);
|
|
992
1034
|
},
|
|
@@ -1018,7 +1060,8 @@ const addChat = {
|
|
|
1018
1060
|
},
|
|
1019
1061
|
{ step: "Reuse the host app's existing visual system for chat UI.", evidence: designEvidence },
|
|
1020
1062
|
{ step: "Add message send with error handling and auth gate. Observe each message's syncState and surface failed sends (error state) with a retry/delete affordance instead of rendering optimistic success unconditionally; clean up unrecoverable failures (e.g. deleteFailedMessages) on init.", evidence: ["implementationRules.file-specific edits"] },
|
|
1021
|
-
{ step: "Render unread counts/badges from the SDK on channel rows or the chat tab (`channel.getUnreadCount()`, `getSubChannelsUnreadCount()`, or `AmityCoreClient.observeUserUnread()` / `getTotalChannelUnread()`), and declare
|
|
1063
|
+
{ step: "Render the chat inbox from SDK channel queries/live collections. Show unread counts/badges from the SDK on channel rows or the chat tab (`channel.getUnreadCount()`, `getSubChannelsUnreadCount()`, or `AmityCoreClient.observeUserUnread()` / `getTotalChannelUnread()`), and declare channel-list sorting explicitly (typically last activity descending) so recency cannot be reversed by UI defaults.", evidence: ["social-plus-sdk/chat/channels", "intake.chat_inbox_scope", "capabilityAvailability.available"] },
|
|
1064
|
+
{ step: "Declare message order explicitly with `AmityMessageQuerySortOption.FIRST_CREATED` or `LAST_CREATED` so the message thread cannot invert by relying on defaults.", evidence: ["social-plus-sdk/chat/messages"] },
|
|
1022
1065
|
{ step: "Wire read receipts and typing indicators if required — and mark the channel/messages read on open (channel.markAsRead() / message.markRead(), or startMessageReceiptSync paired with stopMessageReceiptSync on the view lifecycle) so the server-side unread count actually decrements. Reading the unread count without ever marking read leaves the badge stuck.", evidence: ["requiredInputs.read receipt and typing indicator requirements"] },
|
|
1023
1066
|
{ step: "Add moderation affordance on messages.", evidence: ["requiredInputs.moderation flow for chat messages"] },
|
|
1024
1067
|
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
@@ -1029,6 +1072,8 @@ const addChat = {
|
|
|
1029
1072
|
"chat.message-observer-cleanup",
|
|
1030
1073
|
"chat.send-error-handling",
|
|
1031
1074
|
"chat.channel-shape-matched",
|
|
1075
|
+
"chat.channel-list-live",
|
|
1076
|
+
"chat.channel-list-order-explicit",
|
|
1032
1077
|
"moderation.affordance-present",
|
|
1033
1078
|
"unread.server-synced",
|
|
1034
1079
|
"pagination.cursor-opaque",
|
|
@@ -1156,10 +1201,12 @@ const addCommunity = {
|
|
|
1156
1201
|
];
|
|
1157
1202
|
},
|
|
1158
1203
|
validation: () => [
|
|
1159
|
-
"community
|
|
1160
|
-
"
|
|
1161
|
-
"
|
|
1162
|
-
"community
|
|
1204
|
+
"community.target-resolved",
|
|
1205
|
+
"community.members-live",
|
|
1206
|
+
"community.privacy-flow-explicit",
|
|
1207
|
+
"community.avatar-from-sdk",
|
|
1208
|
+
"community.display-name-from-sdk",
|
|
1209
|
+
"moderation.role-gated-action",
|
|
1163
1210
|
"validate_setup",
|
|
1164
1211
|
"run_sensors",
|
|
1165
1212
|
],
|
|
@@ -1240,6 +1287,14 @@ const addFollow = {
|
|
|
1240
1287
|
required: true,
|
|
1241
1288
|
blocksImplementationWhenMissing: true,
|
|
1242
1289
|
},
|
|
1290
|
+
{
|
|
1291
|
+
id: "profile_identity_scope",
|
|
1292
|
+
question: "Should profile screens show SDK-backed identity, avatar, follower/following counts, and relationship state?",
|
|
1293
|
+
why: "Profile screens often keep placeholder counts or initials while follow state works elsewhere. The agent should confirm which profile fields are in scope and source them from the SDK.",
|
|
1294
|
+
required: false,
|
|
1295
|
+
blocksImplementationWhenMissing: false,
|
|
1296
|
+
options: ["identity + avatar + counts + relationship", "counts only", "relationship only", "custom subset"],
|
|
1297
|
+
},
|
|
1243
1298
|
];
|
|
1244
1299
|
return filterAnswered(ctx.answers, questions);
|
|
1245
1300
|
},
|
|
@@ -1269,17 +1324,19 @@ const addFollow = {
|
|
|
1269
1324
|
evidence: ["social-plus-sdk/social/user-relationship/following/get-follower-following-list", liveDataPlatformPath(ctx.platform)],
|
|
1270
1325
|
},
|
|
1271
1326
|
{ step: "Handle the follow-request approval flow (pending/accept/decline) when following is not automatic.", evidence: ["requiredInputs.follow model"] },
|
|
1327
|
+
{ step: "Populate profile identity from the SDK user/profile object: displayName/userId, SDK avatar URL with initials only as fallback, follower/following counts, and relationship state. Do not ship placeholder count labels or generated initials as the only avatar source.", evidence: ["social-plus-sdk/social/user-profile", "intake.profile_identity_scope", "capabilityAvailability.available"] },
|
|
1272
1328
|
{ step: "Wire block/unblock and a blocked-users surface where in scope.", evidence: ["social-plus-sdk/social/user-relationship/blocking/block-unblock-user"] },
|
|
1273
1329
|
{ step: "Reuse the host app's existing visual system; implement loading/empty/error/data states.", evidence: designEvidence },
|
|
1274
1330
|
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
1275
1331
|
];
|
|
1276
1332
|
},
|
|
1277
1333
|
validation: () => [
|
|
1278
|
-
"
|
|
1279
|
-
"follow
|
|
1334
|
+
"follow.target-resolved",
|
|
1335
|
+
"follow.relationship-live",
|
|
1336
|
+
"follow.model-explicit",
|
|
1337
|
+
"profile.identity-from-sdk",
|
|
1338
|
+
"profile.avatar-from-sdk",
|
|
1280
1339
|
"profile.social-counts",
|
|
1281
|
-
"no invented userId",
|
|
1282
|
-
"relationship observer cleaned up on lifecycle end",
|
|
1283
1340
|
"validate_setup",
|
|
1284
1341
|
"run_sensors",
|
|
1285
1342
|
],
|
|
@@ -1373,9 +1430,9 @@ const addNotifications = {
|
|
|
1373
1430
|
];
|
|
1374
1431
|
},
|
|
1375
1432
|
validation: () => [
|
|
1376
|
-
"
|
|
1377
|
-
"
|
|
1378
|
-
"
|
|
1433
|
+
"notifications.tray-live",
|
|
1434
|
+
"notifications.mark-seen",
|
|
1435
|
+
"notifications.preferences-respected",
|
|
1379
1436
|
"this is in-app tray, not push setup",
|
|
1380
1437
|
"validate_setup",
|
|
1381
1438
|
"run_sensors",
|
|
@@ -8,15 +8,36 @@ export const PRODUCT_EXPECTATION_TITLES = {
|
|
|
8
8
|
"unread.server-synced": "Unread counts use the server-synced stream",
|
|
9
9
|
"feed.rich-post-rendering": "Feed renders rich post types",
|
|
10
10
|
"feed.rich-post-composer-scope": "Feed composer surfaces rich post scope",
|
|
11
|
+
"feed.post-type-scope-explicit": "Feed post-type scope is explicit",
|
|
12
|
+
"feed.composer-type-scope-explicit": "Post composer type scope is explicit",
|
|
11
13
|
"comments.target-resolved": "Comment target comes from the parent entity",
|
|
12
14
|
"comments.thread-read-write": "Comment threads support reading and creation",
|
|
15
|
+
"comments.list-live": "Comment tray lists comments live",
|
|
16
|
+
"comments.creation-affordance": "Comment tray includes comment creation",
|
|
17
|
+
"comments.ui-states-present": "Comment tray renders loading, empty, and error states",
|
|
13
18
|
"chat.channel-target-resolved": "Chat channel comes from app state",
|
|
14
19
|
"chat.message-observer-cleanup": "Chat message observers clean up on lifecycle end",
|
|
15
20
|
"chat.send-error-handling": "Chat send failures are handled",
|
|
16
21
|
"chat.channel-shape-matched": "Chat channel type matches the requested shape",
|
|
22
|
+
"chat.channel-list-live": "Chat channel list stays live",
|
|
23
|
+
"chat.channel-list-order-explicit": "Chat channel list sorting is explicit",
|
|
17
24
|
"chat.unread-visible": "Chat unread counts are visible",
|
|
18
25
|
"chat.message-order-explicit": "Chat message order is explicit",
|
|
26
|
+
"community.target-resolved": "Community target comes from app state",
|
|
27
|
+
"community.members-live": "Community and member lists stay live",
|
|
28
|
+
"community.privacy-flow-explicit": "Community privacy flow is explicit",
|
|
29
|
+
"community.avatar-from-sdk": "Community avatars come from the SDK",
|
|
30
|
+
"community.display-name-from-sdk": "Community display names come from the SDK",
|
|
31
|
+
"moderation.role-gated-action": "Moderator-only actions are role gated",
|
|
32
|
+
"follow.target-resolved": "Follow target comes from app state",
|
|
33
|
+
"follow.relationship-live": "Follow relationships stay live",
|
|
34
|
+
"follow.model-explicit": "Follow approval model is explicit",
|
|
35
|
+
"profile.identity-from-sdk": "Profile identity comes from the SDK",
|
|
36
|
+
"profile.avatar-from-sdk": "Profile avatar comes from the SDK",
|
|
19
37
|
"profile.social-counts": "Profile social counts come from the SDK",
|
|
38
|
+
"notifications.tray-live": "Notification tray is observed live",
|
|
39
|
+
"notifications.mark-seen": "Notification seen state clears server-side",
|
|
40
|
+
"notifications.preferences-respected": "Notification preferences are respected",
|
|
20
41
|
};
|
|
21
42
|
const platformBindings = (expectationId, sensorsByPlatform) => Object.entries(sensorsByPlatform).flatMap(([platform, sensors]) => (Array.isArray(sensors) ? sensors : [sensors]).map((sensorId) => ({
|
|
22
43
|
expectationId,
|
|
@@ -358,6 +379,27 @@ export const PRODUCT_EXPECTATION_BINDINGS = [
|
|
|
358
379
|
sensorId: "ios.chat.sort-explicit",
|
|
359
380
|
platform: "ios",
|
|
360
381
|
},
|
|
382
|
+
...platformBindings("community.avatar-from-sdk", {
|
|
383
|
+
typescript: "typescript.community.avatar-from-sdk",
|
|
384
|
+
"react-native": "react-native.community.avatar-from-sdk",
|
|
385
|
+
android: "android.community.avatar-from-sdk",
|
|
386
|
+
flutter: "flutter.community.avatar-from-sdk",
|
|
387
|
+
ios: "ios.community.avatar-from-sdk",
|
|
388
|
+
}),
|
|
389
|
+
...platformBindings("community.display-name-from-sdk", {
|
|
390
|
+
typescript: "typescript.community.display-name-from-sdk",
|
|
391
|
+
"react-native": "react-native.community.display-name-from-sdk",
|
|
392
|
+
android: "android.community.display-name-from-sdk",
|
|
393
|
+
flutter: "flutter.community.display-name-from-sdk",
|
|
394
|
+
ios: "ios.community.display-name-from-sdk",
|
|
395
|
+
}),
|
|
396
|
+
...platformBindings("moderation.role-gated-action", {
|
|
397
|
+
typescript: "typescript.moderation.role-gated-action",
|
|
398
|
+
"react-native": "react-native.moderation.role-gated-action",
|
|
399
|
+
android: "android.moderation.role-gated-action",
|
|
400
|
+
flutter: "flutter.moderation.role-gated-action",
|
|
401
|
+
ios: "ios.moderation.role-gated-action",
|
|
402
|
+
}),
|
|
361
403
|
{
|
|
362
404
|
expectationId: "profile.social-counts",
|
|
363
405
|
sensorId: "typescript.profile.social-counts-from-sdk",
|
package/package.json
CHANGED
package/rules/feed.yaml
CHANGED
|
@@ -2595,6 +2595,7 @@
|
|
|
2595
2595
|
"outcomes": [
|
|
2596
2596
|
"add-feed",
|
|
2597
2597
|
"add-comments",
|
|
2598
|
+
"add-community",
|
|
2598
2599
|
"validate-setup"
|
|
2599
2600
|
]
|
|
2600
2601
|
},
|
|
@@ -2632,6 +2633,7 @@
|
|
|
2632
2633
|
"outcomes": [
|
|
2633
2634
|
"add-feed",
|
|
2634
2635
|
"add-comments",
|
|
2636
|
+
"add-community",
|
|
2635
2637
|
"validate-setup"
|
|
2636
2638
|
]
|
|
2637
2639
|
},
|
|
@@ -2669,6 +2671,7 @@
|
|
|
2669
2671
|
"outcomes": [
|
|
2670
2672
|
"add-feed",
|
|
2671
2673
|
"add-comments",
|
|
2674
|
+
"add-community",
|
|
2672
2675
|
"validate-setup"
|
|
2673
2676
|
]
|
|
2674
2677
|
},
|
|
@@ -2706,6 +2709,7 @@
|
|
|
2706
2709
|
"outcomes": [
|
|
2707
2710
|
"add-feed",
|
|
2708
2711
|
"add-comments",
|
|
2712
|
+
"add-community",
|
|
2709
2713
|
"validate-setup"
|
|
2710
2714
|
]
|
|
2711
2715
|
},
|
|
@@ -2743,6 +2747,7 @@
|
|
|
2743
2747
|
"outcomes": [
|
|
2744
2748
|
"add-feed",
|
|
2745
2749
|
"add-comments",
|
|
2750
|
+
"add-community",
|
|
2746
2751
|
"validate-setup"
|
|
2747
2752
|
]
|
|
2748
2753
|
},
|
|
@@ -2837,7 +2842,7 @@
|
|
|
2837
2842
|
"rationale": "Using a raw communityId as a displayName fallback (e.g. when passing only the ID in navigation state) shows a UUID to users instead of the community's actual name.",
|
|
2838
2843
|
"applies_when": {
|
|
2839
2844
|
"platforms": ["typescript"],
|
|
2840
|
-
"outcomes": ["add-feed", "add-comments", "validate-setup"]
|
|
2845
|
+
"outcomes": ["add-feed", "add-comments", "add-community", "validate-setup"]
|
|
2841
2846
|
},
|
|
2842
2847
|
"enforcement": {
|
|
2843
2848
|
"deterministic": [
|
|
@@ -2961,7 +2966,7 @@
|
|
|
2961
2966
|
"rationale": "Using a raw communityId as a displayName fallback (e.g. when passing only the ID in navigation state) shows a UUID to users instead of the community's actual name.",
|
|
2962
2967
|
"applies_when": {
|
|
2963
2968
|
"platforms": ["react-native"],
|
|
2964
|
-
"outcomes": ["add-feed", "add-comments", "validate-setup"]
|
|
2969
|
+
"outcomes": ["add-feed", "add-comments", "add-community", "validate-setup"]
|
|
2965
2970
|
},
|
|
2966
2971
|
"enforcement": {
|
|
2967
2972
|
"deterministic": [
|
|
@@ -3085,7 +3090,7 @@
|
|
|
3085
3090
|
"rationale": "Using a raw communityId as a displayName fallback (e.g. when passing only the ID in navigation state) shows a UUID to users instead of the community's actual name.",
|
|
3086
3091
|
"applies_when": {
|
|
3087
3092
|
"platforms": ["flutter"],
|
|
3088
|
-
"outcomes": ["add-feed", "add-comments", "validate-setup"]
|
|
3093
|
+
"outcomes": ["add-feed", "add-comments", "add-community", "validate-setup"]
|
|
3089
3094
|
},
|
|
3090
3095
|
"enforcement": {
|
|
3091
3096
|
"deterministic": [
|
|
@@ -3178,7 +3183,7 @@
|
|
|
3178
3183
|
"rationale": "Using a raw communityId as a displayName fallback (e.g. when passing only the ID in navigation state) shows a UUID to users instead of the community's actual name.",
|
|
3179
3184
|
"applies_when": {
|
|
3180
3185
|
"platforms": ["ios"],
|
|
3181
|
-
"outcomes": ["add-feed", "add-comments", "validate-setup"]
|
|
3186
|
+
"outcomes": ["add-feed", "add-comments", "add-community", "validate-setup"]
|
|
3182
3187
|
},
|
|
3183
3188
|
"enforcement": {
|
|
3184
3189
|
"deterministic": [
|
|
@@ -3302,7 +3307,7 @@
|
|
|
3302
3307
|
"rationale": "Using a raw communityId as a displayName fallback (e.g. when passing only the ID in navigation state) shows a UUID to users instead of the community's actual name.",
|
|
3303
3308
|
"applies_when": {
|
|
3304
3309
|
"platforms": ["android"],
|
|
3305
|
-
"outcomes": ["add-feed", "add-comments", "validate-setup"]
|
|
3310
|
+
"outcomes": ["add-feed", "add-comments", "add-community", "validate-setup"]
|
|
3306
3311
|
},
|
|
3307
3312
|
"enforcement": {
|
|
3308
3313
|
"deterministic": [
|
package/rules/moderation.yaml
CHANGED
|
@@ -733,6 +733,7 @@
|
|
|
733
733
|
"typescript"
|
|
734
734
|
],
|
|
735
735
|
"outcomes": [
|
|
736
|
+
"add-community",
|
|
736
737
|
"add-moderation",
|
|
737
738
|
"validate-setup"
|
|
738
739
|
]
|
|
@@ -769,6 +770,7 @@
|
|
|
769
770
|
"react-native"
|
|
770
771
|
],
|
|
771
772
|
"outcomes": [
|
|
773
|
+
"add-community",
|
|
772
774
|
"add-moderation",
|
|
773
775
|
"validate-setup"
|
|
774
776
|
]
|
|
@@ -805,6 +807,7 @@
|
|
|
805
807
|
"android"
|
|
806
808
|
],
|
|
807
809
|
"outcomes": [
|
|
810
|
+
"add-community",
|
|
808
811
|
"add-moderation",
|
|
809
812
|
"validate-setup"
|
|
810
813
|
]
|
|
@@ -841,6 +844,7 @@
|
|
|
841
844
|
"flutter"
|
|
842
845
|
],
|
|
843
846
|
"outcomes": [
|
|
847
|
+
"add-community",
|
|
844
848
|
"add-moderation",
|
|
845
849
|
"validate-setup"
|
|
846
850
|
]
|
|
@@ -877,6 +881,7 @@
|
|
|
877
881
|
"ios"
|
|
878
882
|
],
|
|
879
883
|
"outcomes": [
|
|
884
|
+
"add-community",
|
|
880
885
|
"add-moderation",
|
|
881
886
|
"validate-setup"
|
|
882
887
|
]
|