@amityco/social-plus-vise 0.14.20 → 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 +10 -0
- package/README.md +3 -3
- package/dist/capabilities.js +142 -0
- package/dist/outcomes.js +61 -6
- package/dist/productExpectations.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ 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
|
+
|
|
7
17
|
## 0.14.20 — 2026-06-05
|
|
8
18
|
|
|
9
19
|
### 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",
|
|
@@ -814,6 +928,34 @@ export const SHARED_PRODUCT_EXPECTATIONS = [
|
|
|
814
928
|
deterministicPlatforms: [],
|
|
815
929
|
hint: "make automatic follow vs pending approval explicit, including accept/decline states where the SDK exposes them",
|
|
816
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
|
+
},
|
|
817
959
|
{
|
|
818
960
|
id: "profile.social-counts",
|
|
819
961
|
label: "Profile follower/following counts",
|
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",
|
|
@@ -988,6 +1021,14 @@ const addChat = {
|
|
|
988
1021
|
required: true,
|
|
989
1022
|
blocksImplementationWhenMissing: false,
|
|
990
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
|
+
},
|
|
991
1032
|
];
|
|
992
1033
|
return filterAnswered(ctx.answers, questions);
|
|
993
1034
|
},
|
|
@@ -1019,7 +1060,8 @@ const addChat = {
|
|
|
1019
1060
|
},
|
|
1020
1061
|
{ step: "Reuse the host app's existing visual system for chat UI.", evidence: designEvidence },
|
|
1021
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"] },
|
|
1022
|
-
{ 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"] },
|
|
1023
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"] },
|
|
1024
1066
|
{ step: "Add moderation affordance on messages.", evidence: ["requiredInputs.moderation flow for chat messages"] },
|
|
1025
1067
|
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
@@ -1030,6 +1072,8 @@ const addChat = {
|
|
|
1030
1072
|
"chat.message-observer-cleanup",
|
|
1031
1073
|
"chat.send-error-handling",
|
|
1032
1074
|
"chat.channel-shape-matched",
|
|
1075
|
+
"chat.channel-list-live",
|
|
1076
|
+
"chat.channel-list-order-explicit",
|
|
1033
1077
|
"moderation.affordance-present",
|
|
1034
1078
|
"unread.server-synced",
|
|
1035
1079
|
"pagination.cursor-opaque",
|
|
@@ -1243,6 +1287,14 @@ const addFollow = {
|
|
|
1243
1287
|
required: true,
|
|
1244
1288
|
blocksImplementationWhenMissing: true,
|
|
1245
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
|
+
},
|
|
1246
1298
|
];
|
|
1247
1299
|
return filterAnswered(ctx.answers, questions);
|
|
1248
1300
|
},
|
|
@@ -1272,6 +1324,7 @@ const addFollow = {
|
|
|
1272
1324
|
evidence: ["social-plus-sdk/social/user-relationship/following/get-follower-following-list", liveDataPlatformPath(ctx.platform)],
|
|
1273
1325
|
},
|
|
1274
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"] },
|
|
1275
1328
|
{ step: "Wire block/unblock and a blocked-users surface where in scope.", evidence: ["social-plus-sdk/social/user-relationship/blocking/block-unblock-user"] },
|
|
1276
1329
|
{ step: "Reuse the host app's existing visual system; implement loading/empty/error/data states.", evidence: designEvidence },
|
|
1277
1330
|
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
@@ -1281,6 +1334,8 @@ const addFollow = {
|
|
|
1281
1334
|
"follow.target-resolved",
|
|
1282
1335
|
"follow.relationship-live",
|
|
1283
1336
|
"follow.model-explicit",
|
|
1337
|
+
"profile.identity-from-sdk",
|
|
1338
|
+
"profile.avatar-from-sdk",
|
|
1284
1339
|
"profile.social-counts",
|
|
1285
1340
|
"validate_setup",
|
|
1286
1341
|
"run_sensors",
|
|
@@ -8,12 +8,19 @@ 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",
|
|
19
26
|
"community.target-resolved": "Community target comes from app state",
|
|
@@ -25,6 +32,8 @@ export const PRODUCT_EXPECTATION_TITLES = {
|
|
|
25
32
|
"follow.target-resolved": "Follow target comes from app state",
|
|
26
33
|
"follow.relationship-live": "Follow relationships stay live",
|
|
27
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",
|
|
28
37
|
"profile.social-counts": "Profile social counts come from the SDK",
|
|
29
38
|
"notifications.tray-live": "Notification tray is observed live",
|
|
30
39
|
"notifications.mark-seen": "Notification seen state clears server-side",
|
package/package.json
CHANGED