@amityco/social-plus-vise 0.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.
- package/LICENSE +51 -0
- package/README.md +92 -0
- package/dist/outcomes.js +574 -0
- package/dist/server.js +810 -0
- package/dist/tools/compliance.js +965 -0
- package/dist/tools/docs.js +312 -0
- package/dist/tools/harness.js +229 -0
- package/dist/tools/integration.js +332 -0
- package/dist/tools/patch.js +67 -0
- package/dist/tools/project.js +908 -0
- package/dist/tools/resolve.js +120 -0
- package/dist/tools/sensors.js +185 -0
- package/dist/types.js +31 -0
- package/dist/version.js +19 -0
- package/package.json +64 -0
- package/rules/design.yaml +66 -0
- package/rules/feed.yaml +126 -0
- package/rules/live-data.yaml +66 -0
- package/rules/push.yaml +95 -0
- package/rules/sdk-lifecycle.yaml +422 -0
- package/rules/security.yaml +162 -0
- package/skills/social-plus-vise/SKILL.md +199 -0
package/dist/outcomes.js
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
export function hasAnswer(answers, id) {
|
|
2
|
+
const value = answers[id];
|
|
3
|
+
return typeof value === "string" && value.trim() !== "";
|
|
4
|
+
}
|
|
5
|
+
const CLASSIFY_ORDER = [
|
|
6
|
+
"setup-push",
|
|
7
|
+
"setup-live-data",
|
|
8
|
+
"add-feed",
|
|
9
|
+
"troubleshoot",
|
|
10
|
+
"validate-setup",
|
|
11
|
+
"setup-sdk",
|
|
12
|
+
];
|
|
13
|
+
const PUSH_PATTERNS = [/\b(push|notification|firebase|fcm|apns)\b/];
|
|
14
|
+
const LIVE_PATTERNS = [
|
|
15
|
+
/\b(live object|live objects|live collection|live collections|realtime collection|real-time collection|observe|observer|subscribe|subscription|unsubscribe|live update|live updates)\b/,
|
|
16
|
+
];
|
|
17
|
+
const FEED_PATTERNS = [
|
|
18
|
+
/\b(social feature|social features|feed|timeline|post list|news feed|create post|create a post|post creation|compose post)\b/,
|
|
19
|
+
];
|
|
20
|
+
const TROUBLESHOOT_PATTERNS = [/\b(error|broken|crash|not working|fail|timeout|401|403)\b/];
|
|
21
|
+
const VALIDATE_PATTERNS = [/\b(validate|check|correct|setup right|initiali[sz])\b/];
|
|
22
|
+
const SETUP_PATTERNS = [/\b(setup|set up|install|integrate|wire|configure)\b/];
|
|
23
|
+
export const BROAD_SOCIAL_REGEX = /\b(nice|social features|social feature|engagement|community experience)\b/i;
|
|
24
|
+
export const DESIGN_REGEX = /\bdesign token|design tokens|theme|same design|design system|brand/i;
|
|
25
|
+
export function classifyOutcome(request) {
|
|
26
|
+
const normalized = request.toLowerCase();
|
|
27
|
+
for (const id of CLASSIFY_ORDER) {
|
|
28
|
+
const def = outcomeRegistry[id];
|
|
29
|
+
if (def.patterns.some((pattern) => pattern.test(normalized))) {
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return "unknown";
|
|
34
|
+
}
|
|
35
|
+
export function getOutcomeDefinition(outcome) {
|
|
36
|
+
return outcomeRegistry[outcome];
|
|
37
|
+
}
|
|
38
|
+
const SUPPORTED_PLATFORMS_FOR_PATH_AUDIT = ["android", "flutter", "ios", "typescript", "react-native"];
|
|
39
|
+
export function knownDocPaths() {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
for (const outcome of Object.keys(outcomeRegistry)) {
|
|
42
|
+
const definition = outcomeRegistry[outcome];
|
|
43
|
+
for (const platform of SUPPORTED_PLATFORMS_FOR_PATH_AUDIT) {
|
|
44
|
+
for (const doc of definition.docs(platform)) {
|
|
45
|
+
if (doc.path && doc.path !== "unknown") {
|
|
46
|
+
seen.add(doc.path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Array.from(seen).sort();
|
|
52
|
+
}
|
|
53
|
+
export function planContextFor(args) {
|
|
54
|
+
return {
|
|
55
|
+
request: args.request,
|
|
56
|
+
outcome: args.outcome,
|
|
57
|
+
platform: args.platform,
|
|
58
|
+
platforms: args.platforms,
|
|
59
|
+
designSignals: args.designSignals,
|
|
60
|
+
mentionsDesign: DESIGN_REGEX.test(args.request),
|
|
61
|
+
broadSocialRequest: BROAD_SOCIAL_REGEX.test(args.request),
|
|
62
|
+
answers: args.answers ?? {},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function platformQuickStart(platform) {
|
|
66
|
+
const paths = {
|
|
67
|
+
android: "social-plus-sdk/getting-started/platform-setup/mobile/android-quick-start",
|
|
68
|
+
flutter: "social-plus-sdk/getting-started/platform-setup/mobile/flutter-quick-start",
|
|
69
|
+
ios: "social-plus-sdk/getting-started/platform-setup/mobile/ios-quick-start",
|
|
70
|
+
typescript: "social-plus-sdk/getting-started/platform-setup/web/web-quick-start",
|
|
71
|
+
"react-native": "social-plus-sdk/getting-started/platform-setup/web/web-quick-start",
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
path: paths[platform] ?? "unknown",
|
|
75
|
+
reason: "Canonical SDK setup guidance for the detected platform.",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function pushSetupPath(platform) {
|
|
79
|
+
const paths = {
|
|
80
|
+
android: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/android-setup",
|
|
81
|
+
flutter: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/flutter-setup",
|
|
82
|
+
ios: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/ios-setup",
|
|
83
|
+
typescript: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/web-setup",
|
|
84
|
+
"react-native": "social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/react-native-setup",
|
|
85
|
+
};
|
|
86
|
+
return paths[platform] ?? "unknown";
|
|
87
|
+
}
|
|
88
|
+
function filterAnswered(answers, questions) {
|
|
89
|
+
return questions.filter((question) => !hasAnswer(answers, question.id));
|
|
90
|
+
}
|
|
91
|
+
function filterStops(answers, stops) {
|
|
92
|
+
return stops.filter((stop) => !hasAnswer(answers, stop.id)).map((stop) => stop.text);
|
|
93
|
+
}
|
|
94
|
+
export function liveDataPlatformPath(platform) {
|
|
95
|
+
const paths = {
|
|
96
|
+
android: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/android",
|
|
97
|
+
flutter: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/flutter",
|
|
98
|
+
ios: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/ios",
|
|
99
|
+
typescript: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/typescript",
|
|
100
|
+
"react-native": "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/typescript",
|
|
101
|
+
};
|
|
102
|
+
return paths[platform] ?? "unknown";
|
|
103
|
+
}
|
|
104
|
+
const setupSdk = {
|
|
105
|
+
id: "setup-sdk",
|
|
106
|
+
patterns: SETUP_PATTERNS,
|
|
107
|
+
interpretation: "Implement setup-sdk.",
|
|
108
|
+
docsQuery: (platform) => `${platform} quick start setup`,
|
|
109
|
+
docs: (platform) => [
|
|
110
|
+
platformQuickStart(platform),
|
|
111
|
+
{
|
|
112
|
+
path: "social-plus-sdk/getting-started/authentication",
|
|
113
|
+
reason: "Canonical login, session, and authentication ordering guidance.",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
intakeQuestions: () => [],
|
|
117
|
+
requiredInputs: () => [],
|
|
118
|
+
implementationRules: () => [],
|
|
119
|
+
implementationSteps: (ctx) => [
|
|
120
|
+
{ step: "Verify or add the platform SDK dependency.", evidence: [platformQuickStart(ctx.platform).path, "targetFiles.dependency"] },
|
|
121
|
+
{
|
|
122
|
+
step: "Initialize the social.plus client exactly once with API key and explicit region.",
|
|
123
|
+
evidence: [platformQuickStart(ctx.platform).path, "requiredInputs.social.plus API key local env/config variable", "requiredInputs.social.plus region"],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
step: "Wire login after user identity is known and before social.plus API queries/subscriptions.",
|
|
127
|
+
evidence: ["social-plus-sdk/getting-started/authentication", "requiredInputs.user identity source for login"],
|
|
128
|
+
},
|
|
129
|
+
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
130
|
+
],
|
|
131
|
+
validation: (platform) => [`${platform}.setup.present`, `${platform}.login.present`, `${platform}.region.explicit`],
|
|
132
|
+
stopConditions: () => [],
|
|
133
|
+
resolvePlan: (platform) => {
|
|
134
|
+
if (platform === "android") {
|
|
135
|
+
return [
|
|
136
|
+
{ file: "app/build.gradle or app/build.gradle.kts", intent: "Add the social.plus Android SDK dependency." },
|
|
137
|
+
{ file: "Application class", intent: "Initialize the SDK once at app startup with API key and region." },
|
|
138
|
+
{ file: "Login/auth flow", intent: "Call login after the app has a known user identity." },
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
if (platform === "flutter") {
|
|
142
|
+
return [
|
|
143
|
+
{ file: "pubspec.yaml", intent: "Add the social.plus Flutter dependency." },
|
|
144
|
+
{ file: "lib/main.dart", intent: "Ensure Flutter binding is initialized before SDK setup." },
|
|
145
|
+
{ file: "Login/auth flow", intent: "Call login after the app has a known user identity." },
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
return [{ file: "customer app", intent: "Inspect the app and fetch relevant docs before proposing code changes." }];
|
|
149
|
+
},
|
|
150
|
+
resolveVerification: () => [
|
|
151
|
+
"Confirm SDK setup runs before any social.plus API call.",
|
|
152
|
+
"Confirm the SDK reads the API key from an environment/config placeholder and the region matches the customer's social.plus console project.",
|
|
153
|
+
],
|
|
154
|
+
resolveNotes: () => [],
|
|
155
|
+
};
|
|
156
|
+
const setupPush = {
|
|
157
|
+
id: "setup-push",
|
|
158
|
+
patterns: PUSH_PATTERNS,
|
|
159
|
+
interpretation: "Set up push notifications with platform credentials, user permission/token handling, device registration after login, and unregister on logout or user switch.",
|
|
160
|
+
docsQuery: (platform) => `${platform} push notification setup`,
|
|
161
|
+
docs: (platform) => [
|
|
162
|
+
{
|
|
163
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/overview",
|
|
164
|
+
reason: "Push notification architecture, delivery options, and cross-platform setup overview.",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
path: pushSetupPath(platform),
|
|
168
|
+
reason: "Canonical push notification setup for the detected platform.",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/register-and-unregister-push-notifications-on-a-device",
|
|
172
|
+
reason: "Device registration and logout/user-switch cleanup guidance.",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/push-notifications/settings/overview",
|
|
176
|
+
reason: "Notification preference settings for user/channel/community controls.",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
intakeQuestions: (ctx) => filterAnswered(ctx.answers, [
|
|
180
|
+
{
|
|
181
|
+
id: "push_platform",
|
|
182
|
+
question: "Which push platform and credentials are already configured for this app?",
|
|
183
|
+
why: "Device registration only works after platform push setup is complete in the app and social.plus console.",
|
|
184
|
+
required: true,
|
|
185
|
+
blocksImplementationWhenMissing: true,
|
|
186
|
+
options: ["FCM", "APNS", "web push", "cross-platform FCM/APNS"],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: "device_token_source",
|
|
190
|
+
question: "Where does the host app request notification permission and obtain the device token?",
|
|
191
|
+
why: "The social.plus SDK registers a token; the host app remains responsible for permission prompts and token creation.",
|
|
192
|
+
required: true,
|
|
193
|
+
blocksImplementationWhenMissing: true,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: "logout_user_switch_flow",
|
|
197
|
+
question: "Which logout or user-switch flow should unregister the device token?",
|
|
198
|
+
why: "A registered device is associated with one user at a time, so cleanup prevents notifications going to the wrong user.",
|
|
199
|
+
required: true,
|
|
200
|
+
blocksImplementationWhenMissing: true,
|
|
201
|
+
},
|
|
202
|
+
]),
|
|
203
|
+
requiredInputs: () => [
|
|
204
|
+
"customer-owned Firebase/APNS/Web Push configuration",
|
|
205
|
+
"device token source from the host app",
|
|
206
|
+
"notification permission request flow",
|
|
207
|
+
"device registration and logout/user-switch behavior",
|
|
208
|
+
],
|
|
209
|
+
implementationRules: () => [
|
|
210
|
+
"Do not fabricate Firebase/APNS/Web Push configuration. Use customer-provided push credentials and files only.",
|
|
211
|
+
"Do not register a device token before social.plus login succeeds.",
|
|
212
|
+
"Always unregister the device token on logout or user switch so a device is not tied to the wrong user.",
|
|
213
|
+
"Do not implement platform permission prompts or token creation unless the host app's push library and ownership are identified.",
|
|
214
|
+
],
|
|
215
|
+
implementationSteps: (ctx) => [
|
|
216
|
+
{ step: "Verify SDK setup and login are already correct before push work.", evidence: ["validate_setup", "social-plus-sdk/getting-started/authentication"] },
|
|
217
|
+
{
|
|
218
|
+
step: "Confirm platform push credentials and app-side permission/token ownership.",
|
|
219
|
+
evidence: [
|
|
220
|
+
"requiredInputs.customer-owned Firebase/APNS/Web Push configuration",
|
|
221
|
+
"requiredInputs.device token source from the host app",
|
|
222
|
+
pushSetupPath(ctx.platform),
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
step: "Wire platform push config using only customer-provided Firebase/APNS/Web Push material.",
|
|
227
|
+
evidence: [pushSetupPath(ctx.platform), "requiredInputs.customer-owned Firebase/APNS/Web Push configuration"],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
step: "Register device after login and unregister on logout/user switch.",
|
|
231
|
+
evidence: ["social-plus-sdk/core-concepts/realtime-communication/push-notifications/register-and-unregister-push-notifications-on-a-device"],
|
|
232
|
+
},
|
|
233
|
+
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
234
|
+
],
|
|
235
|
+
validation: (platform) => [
|
|
236
|
+
"push permission and token source identified",
|
|
237
|
+
"device registration after login",
|
|
238
|
+
"unregister on logout/user switch",
|
|
239
|
+
`${platform}.push.unregister.present`,
|
|
240
|
+
],
|
|
241
|
+
stopConditions: (ctx) => filterStops(ctx.answers, [
|
|
242
|
+
{ id: "push_platform", text: "Firebase/APNS/Web Push configuration is not present or not provided by the customer." },
|
|
243
|
+
{ id: "device_token_source", text: "The host app's notification permission and device-token source is unknown." },
|
|
244
|
+
{ id: "logout_user_switch_flow", text: "No logout or user-switch cleanup point is identified for unregistering device tokens." },
|
|
245
|
+
]),
|
|
246
|
+
resolvePlan: () => [
|
|
247
|
+
{ file: "Platform notification config", intent: "Wire customer-owned Firebase/APNS configuration." },
|
|
248
|
+
{ file: "Login/auth flow", intent: "Register the device after login and unregister on logout." },
|
|
249
|
+
],
|
|
250
|
+
resolveVerification: () => [
|
|
251
|
+
"Confirm device registration happens after login.",
|
|
252
|
+
"Confirm unregister runs on logout or user switch.",
|
|
253
|
+
],
|
|
254
|
+
resolveNotes: () => [
|
|
255
|
+
"Push setup usually needs customer-owned Firebase/APNS values; do not invent secrets or config IDs.",
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
const setupLiveData = {
|
|
259
|
+
id: "setup-live-data",
|
|
260
|
+
patterns: LIVE_PATTERNS,
|
|
261
|
+
interpretation: "Set up Live Object or Live Collection observation with loading/error states, granular updates, and cleanup when the view/component lifecycle ends.",
|
|
262
|
+
docsQuery: (platform) => `${platform} live objects live collections`,
|
|
263
|
+
docs: (platform) => [
|
|
264
|
+
{
|
|
265
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview",
|
|
266
|
+
reason: "Live Object and Live Collection lifecycle, snapshots, loading/error states, and cleanup guidance.",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
path: liveDataPlatformPath(platform),
|
|
270
|
+
reason: "Platform-specific Live Object and Live Collection implementation patterns.",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/realtime-events/overview",
|
|
274
|
+
reason: "Real-time event subscription context for live data updates.",
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
intakeQuestions: (ctx) => filterAnswered(ctx.answers, [
|
|
278
|
+
{
|
|
279
|
+
id: "live_data_shape",
|
|
280
|
+
question: "Is the UI observing one object or a collection?",
|
|
281
|
+
why: "Live Objects and Live Collections have different callback shapes, update semantics, and UI handling.",
|
|
282
|
+
required: true,
|
|
283
|
+
blocksImplementationWhenMissing: true,
|
|
284
|
+
options: ["single object", "collection"],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: "live_data_domain",
|
|
288
|
+
question: "Which domain should be observed?",
|
|
289
|
+
why: "The docs and SDK entrypoints differ for posts, comments, messages, channels, users, and communities.",
|
|
290
|
+
required: true,
|
|
291
|
+
blocksImplementationWhenMissing: true,
|
|
292
|
+
options: ["posts", "comments", "messages", "channels", "users", "communities", "stories"],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "lifecycle_owner",
|
|
296
|
+
question: "Which component, screen, ViewModel, or controller owns the subscription lifecycle?",
|
|
297
|
+
why: "Observers must be cleaned up when the UI lifecycle ends to avoid duplicate subscriptions and memory leaks.",
|
|
298
|
+
required: true,
|
|
299
|
+
blocksImplementationWhenMissing: true,
|
|
300
|
+
},
|
|
301
|
+
]),
|
|
302
|
+
requiredInputs: () => [
|
|
303
|
+
"live data shape: single object or collection",
|
|
304
|
+
"lifecycle owner for subscribe/unsubscribe cleanup",
|
|
305
|
+
"observed domain: posts, comments, messages, channels, users, communities, or stories",
|
|
306
|
+
"query/filter/sort and pagination requirements",
|
|
307
|
+
"UI handling for loading, empty, error, insertion, deletion, and modification states",
|
|
308
|
+
],
|
|
309
|
+
implementationRules: () => [
|
|
310
|
+
"Do not replace Live Object or Live Collection observation with polling unless the docs or user explicitly require it.",
|
|
311
|
+
"Always clean up observers/subscriptions when the owning component, screen, ViewModel, or controller is destroyed.",
|
|
312
|
+
"Handle loading and error states from the live data API before rendering data as ready.",
|
|
313
|
+
"For collections, use insertion/deletion/modification changes for targeted UI updates instead of assuming a full list replacement.",
|
|
314
|
+
],
|
|
315
|
+
implementationSteps: (ctx) => [
|
|
316
|
+
{
|
|
317
|
+
step: "Confirm whether the UI needs a Live Object or Live Collection and which domain it observes.",
|
|
318
|
+
evidence: ["requiredInputs.live data shape", "requiredInputs.observed domain"],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
step: "Fetch Live Objects/Collections docs plus platform-specific implementation guidance.",
|
|
322
|
+
evidence: [
|
|
323
|
+
"social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview",
|
|
324
|
+
liveDataPlatformPath(ctx.platform),
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
step: "Implement the subscription in the owning lifecycle scope and render loading, empty, error, and data states.",
|
|
329
|
+
evidence: ["requiredInputs.lifecycle owner for subscribe/unsubscribe cleanup", "implementationRules.loading-error-states"],
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
step: "For collections, apply insertion, deletion, and modification changes without unnecessary full-list rerenders.",
|
|
333
|
+
evidence: ["social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview"],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
step: "Clean up the observer/subscription when the owner unmounts, disposes, or disappears.",
|
|
337
|
+
evidence: ["implementationRules.cleanup", "validate_setup"],
|
|
338
|
+
},
|
|
339
|
+
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
340
|
+
],
|
|
341
|
+
validation: (platform) => [
|
|
342
|
+
"live data owner identified",
|
|
343
|
+
"loading and error states handled",
|
|
344
|
+
"subscription cleanup on lifecycle end",
|
|
345
|
+
`${platform}.live.cleanup`,
|
|
346
|
+
],
|
|
347
|
+
stopConditions: (ctx) => filterStops(ctx.answers, [
|
|
348
|
+
{ id: "live_data_shape", text: "The request does not identify whether the UI needs a Live Object or Live Collection." },
|
|
349
|
+
{ id: "lifecycle_owner", text: "The lifecycle owner for subscription cleanup is unknown." },
|
|
350
|
+
]),
|
|
351
|
+
resolvePlan: () => [
|
|
352
|
+
{
|
|
353
|
+
file: "Target screen/component/controller",
|
|
354
|
+
intent: "Observe the Live Object or Live Collection and render loading, empty, error, and data states.",
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
file: "Data source/repository module",
|
|
358
|
+
intent: "Encapsulate get/query calls and clean up subscriptions when the lifecycle owner ends.",
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
resolveVerification: () => [
|
|
362
|
+
"Confirm Live Object/Collection observers clean up on unmount/dispose/disappear.",
|
|
363
|
+
"Confirm loading and error states are rendered before data is treated as ready.",
|
|
364
|
+
],
|
|
365
|
+
resolveNotes: () => [
|
|
366
|
+
"Live Object and Live Collection work needs an explicit lifecycle owner so observers can be cleaned up.",
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
const addFeed = {
|
|
370
|
+
id: "add-feed",
|
|
371
|
+
patterns: FEED_PATTERNS,
|
|
372
|
+
interpretation: "Add a social feed or social content surface using the customer app's existing design system.",
|
|
373
|
+
docsQuery: (platform) => `${platform} social feed posts`,
|
|
374
|
+
docs: (platform) => [
|
|
375
|
+
{
|
|
376
|
+
path: "social-plus-sdk/social/posts",
|
|
377
|
+
reason: "Canonical social post/feed concepts and API entrypoint.",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview",
|
|
381
|
+
reason: "Live Object and Live Collection lifecycle, snapshots, loading/error states, and cleanup guidance.",
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
path: liveDataPlatformPath(platform),
|
|
385
|
+
reason: "Platform-specific Live Object and Live Collection implementation patterns.",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
path: "social-plus-sdk/core-concepts/realtime-communication/realtime-events/overview",
|
|
389
|
+
reason: "Real-time event subscription context for live data updates.",
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
intakeQuestions: (ctx) => {
|
|
393
|
+
const questions = [];
|
|
394
|
+
if (ctx.broadSocialRequest) {
|
|
395
|
+
questions.push({
|
|
396
|
+
id: "feature_surface",
|
|
397
|
+
question: "Which social surface should be implemented first?",
|
|
398
|
+
why: "The request is broad, so the coding agent needs one product surface before it can choose SDK docs, UI states, and target files.",
|
|
399
|
+
required: true,
|
|
400
|
+
blocksImplementationWhenMissing: true,
|
|
401
|
+
options: ["feed", "profile", "community", "chat", "notifications"],
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
questions.push({
|
|
405
|
+
id: "feed_scope",
|
|
406
|
+
question: "What scope should the first feed use?",
|
|
407
|
+
why: "Feed APIs and query shape depend on whether the content is global, user-specific, or community-specific.",
|
|
408
|
+
required: true,
|
|
409
|
+
blocksImplementationWhenMissing: true,
|
|
410
|
+
options: ["global", "user", "community"],
|
|
411
|
+
});
|
|
412
|
+
questions.push({
|
|
413
|
+
id: "feed_target",
|
|
414
|
+
question: "What concrete feed target should reads and post creation use?",
|
|
415
|
+
why: "A coding agent must not invent community IDs, user timeline targets, or global feed assumptions. If this is a community feed, provide the existing communityId or the flow that lets users select/create one.",
|
|
416
|
+
required: true,
|
|
417
|
+
blocksImplementationWhenMissing: true,
|
|
418
|
+
options: ["existing communityId", "current user's feed", "selected user's feed", "app-defined/global feed", "ask user at runtime"],
|
|
419
|
+
});
|
|
420
|
+
questions.push({
|
|
421
|
+
id: "target_screen_or_route",
|
|
422
|
+
question: "Which screen, route, or component should receive the social UI?",
|
|
423
|
+
why: "Vise can identify platform files, but the host app owns navigation and product placement.",
|
|
424
|
+
required: true,
|
|
425
|
+
blocksImplementationWhenMissing: true,
|
|
426
|
+
});
|
|
427
|
+
return filterAnswered(ctx.answers, questions);
|
|
428
|
+
},
|
|
429
|
+
requiredInputs: () => [
|
|
430
|
+
"live data shape: single object or collection",
|
|
431
|
+
"lifecycle owner for subscribe/unsubscribe cleanup",
|
|
432
|
+
"feature choice: feed, profile, community, chat, notifications, or another social surface",
|
|
433
|
+
"feed scope: global, user, or community",
|
|
434
|
+
"concrete feed target for reads and post creation: existing communityId, selected user target, current user target, or app-defined feed target",
|
|
435
|
+
"target screen or route for the feed UI",
|
|
436
|
+
],
|
|
437
|
+
implementationRules: () => [
|
|
438
|
+
"Do not invent or hardcode communityId, targetId, feedId, channelId, or any other feed target. Ask for the target or implement a user/customer-owned selection flow.",
|
|
439
|
+
"Do not ask for API keys in chat. Create an env/config placeholder and tell the user to fill the real value locally.",
|
|
440
|
+
],
|
|
441
|
+
implementationSteps: (ctx) => {
|
|
442
|
+
const designEvidence = ctx.designSignals.length > 0
|
|
443
|
+
? ctx.designSignals.map((signal) => signal.file)
|
|
444
|
+
: ["requiredInputs.design token or theme source file"];
|
|
445
|
+
return [
|
|
446
|
+
{
|
|
447
|
+
step: "Confirm the target route/screen, feed scope, and concrete feed target before writing code.",
|
|
448
|
+
evidence: [
|
|
449
|
+
"requiredInputs.feed scope",
|
|
450
|
+
"requiredInputs.concrete feed target",
|
|
451
|
+
"requiredInputs.target screen or route",
|
|
452
|
+
],
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
step: "Fetch the canonical social/feed docs and use platform-appropriate live collection patterns.",
|
|
456
|
+
evidence: [
|
|
457
|
+
"social-plus-sdk/social/posts",
|
|
458
|
+
"social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview",
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
{ step: "Reuse the host app's existing visual system for the social surface.", evidence: designEvidence },
|
|
462
|
+
{ step: "Implement loading, empty, error, and data states.", evidence: ["implementationRules.file-specific edits"] },
|
|
463
|
+
{ step: "Run validate_setup and detected command sensors after edits.", evidence: ["validate_setup", "run_sensors"] },
|
|
464
|
+
];
|
|
465
|
+
},
|
|
466
|
+
validation: (platform) => [
|
|
467
|
+
"feed target identified",
|
|
468
|
+
"no invented communityId/targetId/feedId",
|
|
469
|
+
`${platform}.feed.target.literal`,
|
|
470
|
+
],
|
|
471
|
+
stopConditions: (ctx) => filterStops(ctx.answers, [
|
|
472
|
+
{ id: "feed_target", text: "The concrete feed target is unknown; do not invent communityId, targetId, feedId, or global feed assumptions." },
|
|
473
|
+
{ id: "feed_target", text: "If post creation is in scope, the write target must be provided by the user, existing app state, or a customer-owned selection/create flow." },
|
|
474
|
+
]),
|
|
475
|
+
resolvePlan: () => [
|
|
476
|
+
{
|
|
477
|
+
file: "Target screen/component",
|
|
478
|
+
intent: "Add a feed query and render loading, empty, error, and item states.",
|
|
479
|
+
},
|
|
480
|
+
{ file: "Navigation route", intent: "Expose the feed surface from the existing app navigation." },
|
|
481
|
+
],
|
|
482
|
+
resolveVerification: () => [
|
|
483
|
+
"Confirm the feed screen handles loading, empty, error, and data states.",
|
|
484
|
+
"Confirm the feed query is scoped to the intended global, user, or community feed.",
|
|
485
|
+
],
|
|
486
|
+
resolveNotes: () => [],
|
|
487
|
+
};
|
|
488
|
+
const troubleshoot = {
|
|
489
|
+
id: "troubleshoot",
|
|
490
|
+
patterns: TROUBLESHOOT_PATTERNS,
|
|
491
|
+
interpretation: "Implement troubleshoot.",
|
|
492
|
+
docsQuery: (platform) => `${platform} troubleshooting authentication setup`,
|
|
493
|
+
docs: () => [
|
|
494
|
+
{
|
|
495
|
+
path: "social-plus-sdk/getting-started/authentication",
|
|
496
|
+
reason: "Authentication and session issues are common setup failure causes.",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
path: "social-plus-sdk/core-concepts/foundation/logging",
|
|
500
|
+
reason: "Logging and diagnostics guidance for debugging integration failures.",
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
intakeQuestions: () => [],
|
|
504
|
+
requiredInputs: () => [],
|
|
505
|
+
implementationRules: () => [],
|
|
506
|
+
implementationSteps: () => [
|
|
507
|
+
{ step: "Gather more evidence before implementation.", evidence: ["stopConditions", "search_docs", "inspect_project"] },
|
|
508
|
+
],
|
|
509
|
+
validation: () => [],
|
|
510
|
+
stopConditions: () => [],
|
|
511
|
+
resolvePlan: () => [
|
|
512
|
+
{ file: "customer app", intent: "Inspect the app and fetch relevant docs before proposing code changes." },
|
|
513
|
+
],
|
|
514
|
+
resolveVerification: () => [],
|
|
515
|
+
resolveNotes: () => [],
|
|
516
|
+
};
|
|
517
|
+
const validateSetup = {
|
|
518
|
+
id: "validate-setup",
|
|
519
|
+
patterns: VALIDATE_PATTERNS,
|
|
520
|
+
interpretation: "Implement validate-setup.",
|
|
521
|
+
docsQuery: (platform) => `${platform} quick start setup`,
|
|
522
|
+
docs: (platform) => [
|
|
523
|
+
platformQuickStart(platform),
|
|
524
|
+
{
|
|
525
|
+
path: "social-plus-sdk/getting-started/authentication",
|
|
526
|
+
reason: "Canonical login, session, and authentication ordering guidance.",
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
intakeQuestions: () => [],
|
|
530
|
+
requiredInputs: () => [],
|
|
531
|
+
implementationRules: () => [],
|
|
532
|
+
implementationSteps: () => [
|
|
533
|
+
{ step: "Gather more evidence before implementation.", evidence: ["stopConditions", "search_docs", "inspect_project"] },
|
|
534
|
+
],
|
|
535
|
+
validation: () => [],
|
|
536
|
+
stopConditions: () => [],
|
|
537
|
+
resolvePlan: () => [
|
|
538
|
+
{ file: "customer app", intent: "Inspect the app and fetch relevant docs before proposing code changes." },
|
|
539
|
+
],
|
|
540
|
+
resolveVerification: () => [
|
|
541
|
+
"Confirm SDK setup runs before any social.plus API call.",
|
|
542
|
+
"Confirm the SDK reads the API key from an environment/config placeholder and the region matches the customer's social.plus console project.",
|
|
543
|
+
],
|
|
544
|
+
resolveNotes: () => [],
|
|
545
|
+
};
|
|
546
|
+
const unknown = {
|
|
547
|
+
id: "unknown",
|
|
548
|
+
patterns: [],
|
|
549
|
+
interpretation: "Implement unknown.",
|
|
550
|
+
docsQuery: (platform) => `${platform} quick start setup`,
|
|
551
|
+
docs: () => [],
|
|
552
|
+
intakeQuestions: () => [],
|
|
553
|
+
requiredInputs: () => [],
|
|
554
|
+
implementationRules: () => [],
|
|
555
|
+
implementationSteps: () => [
|
|
556
|
+
{ step: "Gather more evidence before implementation.", evidence: ["stopConditions", "search_docs", "inspect_project"] },
|
|
557
|
+
],
|
|
558
|
+
validation: () => [],
|
|
559
|
+
stopConditions: () => [],
|
|
560
|
+
resolvePlan: () => [
|
|
561
|
+
{ file: "customer app", intent: "Inspect the app and fetch relevant docs before proposing code changes." },
|
|
562
|
+
],
|
|
563
|
+
resolveVerification: () => [],
|
|
564
|
+
resolveNotes: () => [],
|
|
565
|
+
};
|
|
566
|
+
const outcomeRegistry = {
|
|
567
|
+
"setup-sdk": setupSdk,
|
|
568
|
+
"setup-push": setupPush,
|
|
569
|
+
"setup-live-data": setupLiveData,
|
|
570
|
+
"add-feed": addFeed,
|
|
571
|
+
troubleshoot,
|
|
572
|
+
"validate-setup": validateSetup,
|
|
573
|
+
unknown,
|
|
574
|
+
};
|