@amityco/social-plus-vise 0.8.1 → 0.12.2

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.
@@ -217,6 +217,66 @@
217
217
  "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "react-native.logging.no-pii-in-log" }],
218
218
  "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "redaction_policy", "description": "Explanation of how PII is redacted or why the logged value is not real user PII.", "upload_policy": "upload-with-consent" }] }
219
219
  }
220
+ },
221
+ {
222
+ "id": "react-native.secret.env-gitignore",
223
+ "version": 1,
224
+ "title": "React Native env secret files should be ignored",
225
+ "severity": "warning",
226
+ "rationale": "When SDK secrets are loaded from env files, local env files should be ignored so customer API keys are not accidentally committed.",
227
+ "applies_when": { "platforms": ["react-native"], "outcomes": ["setup-sdk", "setup-push", "setup-live-data", "add-feed", "validate-setup"] },
228
+ "enforcement": {
229
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "react-native.secret.env-gitignore" }],
230
+ "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "gitignore_policy", "description": "Where local env files are ignored or otherwise prevented from commit.", "upload_policy": "upload-with-consent" }] }
231
+ }
232
+ },
233
+ {
234
+ "id": "react-native.secret.env-example",
235
+ "version": 1,
236
+ "title": "React Native env secret config should have an example file",
237
+ "severity": "warning",
238
+ "rationale": "A placeholder env example lets agents and developers create local config without asking users to paste secrets into chat.",
239
+ "applies_when": { "platforms": ["react-native"], "outcomes": ["setup-sdk", "setup-push", "setup-live-data", "add-feed", "validate-setup"] },
240
+ "enforcement": {
241
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "react-native.secret.env-example" }],
242
+ "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "template_path", "description": "Path to the committed env/config template with placeholder secret keys.", "upload_policy": "upload-with-consent" }] }
243
+ }
244
+ },
245
+ {
246
+ "id": "flutter.secret.env-gitignore",
247
+ "version": 1,
248
+ "title": "Flutter env secret files should be ignored",
249
+ "severity": "warning",
250
+ "rationale": "When SDK secrets are loaded from .env or secrets.dart, they should be ignored so customer API keys are not accidentally committed.",
251
+ "applies_when": { "platforms": ["flutter"], "outcomes": ["setup-sdk", "setup-push", "setup-live-data", "add-feed", "validate-setup"] },
252
+ "enforcement": {
253
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "flutter.secret.env-gitignore" }],
254
+ "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "gitignore_policy", "description": "Where local env files are ignored or otherwise prevented from commit.", "upload_policy": "upload-with-consent" }] }
255
+ }
256
+ },
257
+ {
258
+ "id": "android.secret.env-gitignore",
259
+ "version": 1,
260
+ "title": "Android env secret files should be ignored",
261
+ "severity": "warning",
262
+ "rationale": "local.properties should be ignored so customer API keys are not accidentally committed.",
263
+ "applies_when": { "platforms": ["android"], "outcomes": ["setup-sdk", "setup-push", "setup-live-data", "add-feed", "validate-setup"] },
264
+ "enforcement": {
265
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "android.secret.env-gitignore" }],
266
+ "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "gitignore_policy", "description": "Where local env files are ignored or otherwise prevented from commit.", "upload_policy": "upload-with-consent" }] }
267
+ }
268
+ },
269
+ {
270
+ "id": "ios.secret.env-gitignore",
271
+ "version": 1,
272
+ "title": "iOS env secret files should be ignored",
273
+ "severity": "warning",
274
+ "rationale": "Secrets.plist and .xcconfig should be ignored so customer API keys are not accidentally committed.",
275
+ "applies_when": { "platforms": ["ios"], "outcomes": ["setup-sdk", "setup-push", "setup-live-data", "add-feed", "validate-setup"] },
276
+ "enforcement": {
277
+ "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "ios.secret.env-gitignore" }],
278
+ "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "gitignore_policy", "description": "Where local env files are ignored or otherwise prevented from commit.", "upload_policy": "upload-with-consent" }] }
279
+ }
220
280
  }
221
281
  ]
222
282
  }
@@ -35,30 +35,23 @@ vise validate .
35
35
  vise run-sensors .
36
36
  ```
37
37
 
38
+ **`vise check .` is mandatory, not optional. You are not done until it passes or every finding is explicitly attested.** Running `vise plan` and `vise inspect` but skipping `vise check` is the most common failure mode — it means the deterministic catch-net never ran and known-bad patterns ship. When you read a `vise plan`, do not truncate it (`| head` drops the implementation steps); read the full `implementationSteps` array.
39
+
38
40
  Treat Vise runtime smoke sensors as real validation. For TypeScript/React Native projects, `vise run-sensors` may include `TypeScript SDK import smoke`; if it fails, the SDK package does not resolve from the host project runtime and the integration is not done.
39
41
 
40
42
  In CI or pull-request pipelines, use `vise check . --ci`. It is read-only, exits non-zero unless compliance is green, and never writes deterministic-pass records; use `vise sync` only during the implementation loop after a local green check.
41
43
 
42
- ## Engagement Scope (Optional)
43
-
44
- If the customer has set up `sp-vise/engagement.json` (via `vise engagement init`), respect the declared scope. `vise engagement show .` prints the engagement metadata: tier, customer id, contracted outcomes, reviewer assignment.
45
-
46
- When `vise init` reports a `warnings` array containing "not in the engagement scope", surface that to the user — don't silently widen scope. The user either extends the scope by editing `engagement.json` or accepts that the current work is out-of-contract.
47
-
48
- If no `engagement.json` exists, do not create one without the user's intent. `vise engagement init` is a contractual artifact; agents should only run it when the user explicitly asks to set up engagement metadata, typically with `--tier`, `--customer-id`, and `--scope`.
44
+ ## Debugging (Day 2 Operations)
49
45
 
50
- If `vise` is not installed, use:
51
-
52
- ```bash
53
- npm install -g @amityco/social-plus-foundry
54
- ```
46
+ If the customer reports an **SDK-specific** runtime crash, build failure, or data-not-loading bug, start with `vise debug .` (with `--error` / `--error-file`) **before** broad repo inspection — do not guess the fix first. Scope limit: only for errors mentioning `social.plus`/`Amity`/`live-collection` or in files importing the SDK; never for generic UI/CSS issues. Full `vise debug` workflow and `repairBrief` format: **see `reference/debugging.md`**.
55
47
 
56
- Then retry the `vise` command.
48
+ (If `vise` isn't installed: `npm install -g @amityco/social-plus-vise`. Engagement-scope handling — `sp-vise/engagement.json` — is in `reference/operations.md`.)
57
49
 
58
50
  ## Stop Instead Of Guessing
59
51
 
60
52
  Ask the user before editing when required values are missing:
61
53
 
54
+ - Next.js rendering mode: whether the integration target is a Server Component, Client Component (`'use client'`), or Pages Router. SDK initialization must be client-side only.
62
55
  - region, auth/user identity, or session-handler ownership (which class/module implements `sessionWillRenewAccessToken`)
63
56
  - feed target, community ID, target ID, feed ID, or channel ID
64
57
  - comment target source (which parent entity provides postId/referenceId)
@@ -109,6 +102,8 @@ Per platform:
109
102
  - Flutter Dart: implement `AmitySessionHandler.sessionWillRenewAccessToken(AmityAccessTokenRenewal)` and call `renewal.renew()`.
110
103
  - iOS Swift: implement `AmitySessionHandler.sessionWillRenewAccessToken(renewal:)` and call `renewal.renew()`.
111
104
 
105
+ **Do not fire SDK collection queries in the same synchronous tick as `login()`.** Gate collection setup behind the SDK's session-active signal (`sessionWillRenewAccessToken` confirming the first token was granted, or an explicit auth-state callback). On a warm local machine login resolves first by chance; on a cold production start the query fires before the session is established and silently returns empty or errors.
106
+
112
107
  ### Session Handler Retention
113
108
 
114
109
  **CRITICAL:** The session handler object must outlive the function that created it. If you declare the session handler as a function-local variable (e.g. inside `setUp()`, `initState()`, `onCreate()`), the host language's garbage collector (Dart, JS, Kotlin) or ARC (iOS) will destroy it before the SDK ever needs to renew the session.
@@ -134,6 +129,15 @@ Amity exposes a global `unreadCount` stream that syncs across devices.
134
129
  - **Correct pattern**: Subscribe to the unread stream provided by the SDK (e.g. `AmityCoreClient.unreadCount()`, `getUnreadCount()`, `unread.stream`).
135
130
  - **Wrong pattern**: Hand-rolling unread counting on the client (e.g. `posts.filter(p => !p.isRead).length`). This causes counts to go out of sync because the server's read state isn't pushed to the manual counter.
136
131
 
132
+ A read *count* with no way to clear it is half a feature: when a channel/thread is opened, mark it read so the server-side count actually decrements (`channel.markAsRead()` / `message.markRead()`, or `startMessageReceiptSync` + `stopMessageReceiptSync` paired with the view lifecycle). Reading the unread stream but never marking read leaves the badge stuck.
133
+
134
+ ### Message Delivery State
135
+
136
+ A sent message starts in a `syncing` state and can land in `error` (e.g. network failure, or after an SDK session restart). A composer that renders only the optimistic success leaves failed messages silently stuck.
137
+
138
+ - **Correct pattern**: Observe each message's `syncState` and surface failed sends with a retry/delete affordance; clean up unrecoverable failures (e.g. `deleteFailedMessages()`) on init.
139
+ - **Wrong pattern**: Calling `createMessage` and rendering success unconditionally, with no handling for the `error` syncState.
140
+
137
141
  ### File Upload & Media
138
142
 
139
143
  Uploading media to external buckets (S3, Cloudinary) directly and trying to attach the resulting URL to an Amity post will fail. The SDK won't recognize external URLs as attachments.
@@ -155,19 +159,42 @@ When querying lists of entities (e.g., `getPosts`, `getCommunityFeed`), always u
155
159
  - **Correct pattern:** Observe the collection (e.g., `.observe()`, `.listen()`, `StreamBuilder`, `LiveData`, `getLiveCollection()`) and render the stream.
156
160
  - **Wrong pattern:** `await getPosts(...)` and assigning to a static array.
157
161
 
162
+ When using a LiveCollection or live object, also handle connection-state changes. The SDK exposes a connection state stream; render a "reconnecting" indicator or disable interaction controls when the connection is lost. An app that silently shows stale data on network drops generates support tickets attributed to the SDK rather than the integration.
163
+
158
164
  ### Post Status Filters
159
165
  Always filter post queries to exclude deleted or flagged content. Amity's older SDKs include flagged/deleted posts by default, which can leak moderated content into the feed UI if unfiltered.
160
166
  - **Correct pattern:** Apply `.includeDeleted(false)`, `.statuses([.published])`, or `.feedType(AmityFeedType.PUBLISHED)` to your query builders.
161
167
 
168
+ ### Post Review Queue
169
+ When a community has review enabled (`ADMIN_REVIEW_POST_REQUIRED`), posts flow through `reviewing` → `published`/`declined` feeds. A moderation dashboard that only queries the `published` feed silently misses everything awaiting review.
170
+ - **Correct pattern:** For a moderator surface, query `feedType: 'reviewing'` (gated on the `REVIEW_COMMUNITY_POST` permission) and wire `approvePost()` / `declinePost()`. A regular member feed stays on `published`.
171
+ - This is completeness, not deterministically enforced (most feeds aren't moderator dashboards) — match it to whether you're building a moderation/review surface.
172
+
162
173
  ### Opaque Pagination Cursors
163
174
  Amity uses opaque, token-based cursors for pagination. Never attempt to construct the `nextPage` argument using numeric math (e.g., `page * size`). This will cause silent failures or HTTP 400 errors.
164
175
  - **Correct pattern:** Pass the exact `nextPageToken` returned from the previous query result, or call `.loadMore()` / `.nextPage()` on the collection itself.
165
176
  - **Wrong pattern:** `.nextPage(pageNumber * 20)`
166
177
 
167
178
  ### Parent-Child Post Rendering
168
- Amity models image and video attachments as "child posts" attached to a parent post. If your UI only renders the parent post's text, it will silently drop all media attachments.
169
- - **Correct pattern:** Ensure the post-rendering component recursively processes `post.children`, `.childrenPosts`, or calls `getChildren()`.
170
- - **Wrong pattern:** `Text(post.data.text)` with no reference to the post's children.
179
+ Amity attaches rich content — **images, videos, files, polls, and rooms/livestreams** as "child posts" under a parent whose own `dataType` is usually `'text'`. A UI that reads only the parent will silently drop all of it. This applies to **every** media type, not just images: resolving images from `childrenPosts` but gating polls/videos/rooms on the parent's dataType is the most common version of this bug.
180
+ - **Correct pattern:** Resolve every media type from the parent **or** its children, consistently — `post.childrenPosts?.find(c => c.dataType === 'poll')?.getPollInfo()`, same for image/video/room/file. Process `post.children` / `.childrenPosts` / `getChildren()` for all of them.
181
+ - **Wrong pattern:** `Text(post.data.text)` with no children handling or handling children for images only while checking `post.dataType === 'poll'/'video'/'room'` (the parent) for the rest.
182
+
183
+ ### Render Every Post Type the Feed Returns
184
+ A global or community feed query without a `dataTypes` filter returns **all** post types — text, image, video, file, poll, livestream, and room. A renderer that branches on only one or two types silently renders the rest as blank cards. This is the single most common "the feed looks broken" complaint.
185
+ - **Correct pattern:** Resolve each media type from **both the parent and its `childrenPosts`** — in a feed the parent is usually `dataType: 'text'` and the image/video/poll/room rides on a *child* post (see Parent-Child Post Rendering). Use a helper per type, e.g. `post.dataType === 'poll' ? post.getPollInfo() : post.childrenPosts?.find(c => c.dataType === 'poll')?.getPollInfo()` — and the same for image/video/room/file. For polls, read each answer via `answer.data` (a plain string) and `answer.image?.fileUrl`, support `votePoll`/`unvotePoll`, and show `poll.closedAt`/`closedIn`. For room/livestream, display the entity's `title`/`status` — never the raw `roomId`. **And when you render the text body itself (post or comment): 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 — don't print raw text and lose the @mention highlight.** (Pass `mentionees` on create so mentioned users are notified.)
186
+ - **Wrong pattern:** Gating media on the **parent's** dataType only — `const poll = post.dataType === 'poll' ? post.getPollInfo() : undefined` — while resolving images from children. The feed parent is `'text'`, so that branch never matches and polls/videos/rooms silently never render. Vise's `posts.parent-child-rendered` flags this inconsistency. Likewise, rendering `data.text` directly with no use of `metadata` mention offsets drops @mention highlights.
187
+ - Coverage of *which* types appear is **not** deterministically enforced (a feed may legitimately contain only some types) — it is your responsibility to match rendering to what the feed can return.
188
+
189
+ ### Read SDK Objects Through Their Accessors, Not Casts
190
+ Casting an SDK return value to a hand-written shape — `(post.data as { text?: string }).text` — silences the type-checker. When an SDK upgrade renames a field, the build still compiles and the bug only appears at runtime.
191
+ - **Correct pattern:** Use the SDK's typed accessors and types (`post.getImageInfo()`, `post.getPollInfo()`, the `Amity.*` types) so `tsc`/`analyze` flags field-level breakage on upgrade.
192
+ - **Wrong pattern:** `as { … }` casts over `post.data`, `comment.data`, `answer.data`, or community/user fields.
193
+
194
+ ### Comment Surfaces Need Read, Write, and an Explicit `pageSize`
195
+ A comment thread is incomplete if it only lists comments. Users expect to post.
196
+ - **Correct pattern:** Pair `CommentRepository.getComments({ …, pageSize: 20 }, cb)` (read) with a composer that calls `createComment` (write), gated behind the user's ban state. Always pass an explicit `pageSize` — the default pagination uses skip/limit, which the server rejects for scrollable queries with a runtime 500000 error. Render replies via `getComments({ parentId })` when `childrenNumber > 0`.
197
+ - **Wrong pattern:** A read-only comment list, or `getComments` with no `pageSize`.
171
198
 
172
199
  ### Explicit Feed Target Types
173
200
  When calling `createPost`, do not hardcode the `targetType` to a literal value like `AmityPostTargetType.COMMUNITY` if the composer component is meant to be reusable.
@@ -196,43 +223,13 @@ Amity routes custom-shape post payloads via the `dataType` tag. Omitting it make
196
223
 
197
224
  ## SDK Version Handling
198
225
 
199
- Do not use floating SDK dependency versions such as `latest`, `*`, `any`, `+`, or branch-based iOS package references. Pin the social.plus SDK to an explicit version or reviewed semver range, then let the host app's package manager lockfile capture the exact resolved version.
200
-
201
- ## Attestation Flow
202
-
203
- `vise check` reports each applicable rule as one of `deterministic-pass`, `attestation-needed`, `failed`, or `blocked`. `attestation-needed` and `failed` are NOT warnings to skip — they block "I'm done." Resolve them in one of two ways:
204
-
205
- 1. **Make the deterministic check fire (preferred).** Re-read `vise explain <rule-id>` for the rule's enforcement details, adjust the code so the check passes, then run `vise check` again. After it goes green, run `vise sync` to persist the deterministic-pass attestation files.
206
- 2. **Record an attestation with evidence.** When the rule cannot fire because the customer's architecture is unconventional (DI wrappers, custom abstractions, etc.), use `vise attest`:
207
-
208
- ```bash
209
- vise attest . \
210
- --rule typescript.client.region \
211
- --confidence high \
212
- --signer host-agent \
213
- --rationale "API_REGIONS.EU is explicitly passed as the second argument to Client.createClient in src/socialSDK.ts." \
214
- --evidence-file ./.tmp/evidence.json
215
- ```
216
-
217
- The evidence file is a JSON object matching the `evidence_required` schema for that rule (`vise explain <rule-id>` shows the required fields). Include concrete source file paths in evidence values whenever the evidence depends on code, for example `{ "region_source": "src/socialSDK.ts" }` or `{ "login_flow": "src/auth/socialSession.ts" }`. `vise attest` fingerprints cited files, and future `vise check` runs mark the attestation stale if those files change.
218
-
219
- ### Confidence honesty
220
-
221
- - `high` — direct evidence the deterministic check would have fired if it had been more flexible. Auto-recorded.
222
- - `medium` — reasoned about it but cannot point to a single unambiguous location. May require human signer depending on rule.
223
- - `low` — guess. Always requires `--signer human`.
224
-
225
- If the rule's `escalation_threshold` is above your confidence, `vise attest --signer host-agent` is rejected. In v0.4 there is no hosted review queue yet, so the path is: stop, surface to the user with the rule id and the evidence you have, and ask them to run `vise attest --signer human --identity <their name or email>` themselves.
226
-
227
- ### What `vise sync` is NOT
226
+ For a **new (greenfield) install**, resolve the **current latest** SDK at install time never copy a version number from examples, docs snippets, or memory, which go stale (e.g. an old major can lack `StoryRepository`). Install latest, then pin the resolved version: `npm install @amityco/ts-sdk@latest` (TypeScript/React Native; the lockfile captures the exact version), or the latest Maven / CocoaPods·SwiftPM / pub.dev release for Android / iOS / Flutter. `vise plan` surfaces an `sdkVersion` field — for TypeScript/React Native it reports the current npm `latest`; **heed it**.
228
227
 
229
- - `vise sync` does **not** accept `--attest` or any flag. It only persists deterministic-pass results.
230
- - Passing unknown flags to `vise sync` will error in v0.4 — don't try to use it as a shortcut for attestation.
231
- - To record an attestation, use `vise attest`. Always.
228
+ Do not use floating SDK dependency versions such as `latest`, `*`, `any`, `+`, or branch-based iOS package references in the committed manifest. Pin the social.plus SDK to an explicit version or reviewed semver range, then let the host app's package manager lockfile capture the exact resolved version. If `vise plan` reports that an **existing** project is on an older major than current latest, treat that as an FYI only a major upgrade is out of scope unless the task explicitly asks for it.
232
229
 
233
- ### Treat warnings as blockers
230
+ ## Resolving `vise check` Findings
234
231
 
235
- If a legacy `vise validate` run reports `needs-review` with warnings, those warnings correspond to compliance rules in `attestation-needed` state. Do not interpret them as "code is fine, just static-analysis noise." Run `vise check` to get the structured rule list, then resolve each one as above.
232
+ `vise check` reports each rule as `deterministic-pass`, `attestation-needed`, `failed`, or `blocked`. **`attestation-needed` and `failed` block "I'm done" — they are not skippable warnings.** Resolve each one of two ways: (1) **preferred** adjust the code so the deterministic check fires (`vise explain <rule-id>` shows what it wants), then `vise check` green `vise sync`; or (2) record `vise attest` with evidence when the architecture is genuinely unconventional. Confidence honesty, the `vise attest` evidence-file format, escalation/human-signer rules, and "`vise sync` is not attestation": **see `reference/operations.md`**.
236
233
 
237
234
  ## Feature Guidance
238
235
 
@@ -245,6 +242,14 @@ vise plan . --request "<feed or post request>"
245
242
 
246
243
  Require a concrete target from the app: current user feed, selected community, selected channel, or another user-provided domain object. Do not hardcode random target IDs.
247
244
 
245
+ **Decide engagement scope explicitly — Vise authors the checklist, you subtract with a reason.** `vise plan` returns a `completenessChecklist` (the canonical capabilities for the outcome, e.g. comments, reactions, pagination, polls, media, moderation) and `vise check` reports each as present / missing / opted-out. This is **advisory — it never fails the check**; it exists so completeness doesn't depend on your memory. For each capability: build it, or explicitly opt out with a recorded marker in the code so the omission is reviewable, not accidental:
246
+
247
+ ```
248
+ // vise: scope-omit poll — text + image feed only; polls disabled for this tenant
249
+ ```
250
+
251
+ Do not silently drop a capability. `vise check`'s `completeness` section will keep nudging on anything that's neither built nor opted-out.
252
+
248
253
  **Demo wiring (when the app needs to compile before the real target source exists):** even at the top-level `App` / `MaterialApp` / `_app.tsx` / `AppDelegate` wiring site, do not pass a literal string. Use the host platform's compile-time env channel so the rule sees no string literal at the call site:
249
254
 
250
255
  - Flutter: `const String.fromEnvironment('AMITY_COMMUNITY_ID')` — value injected via `flutter run --dart-define=AMITY_COMMUNITY_ID=…`
@@ -293,6 +298,11 @@ When rendering moderation actions (like `flagPost`, `deletePost`, `banUser`, `mu
293
298
 
294
299
  Similarly, moderator-only data such as `flagCount` must not be leaked to non-moderators. Rendering `post.flagCount` unconditionally exposes moderation signal to all users, which is a privacy regression. Gating it with a role check ensures it is only visible to authorized users.
295
300
 
301
+ **Post/comment actions are gated by the viewer's relationship to the content**, and ownership is distinct from moderator role:
302
+ - **Author** (`post.postedUserId === currentUserId`): show edit (`editPost`/`updateComment`) and delete (`deletePost`/`deleteComment`).
303
+ - **Non-author**: show report/flag (`flagPost`/`flagComment`).
304
+ - A post card with no actions menu at all is incomplete. Do not show edit/delete to non-authors, and do not rely on the server's 403 to hide them.
305
+
296
306
  ### User Ban State
297
307
 
298
308
  Banned users still see interaction buttons (like `createPost`, `createComment`, `sendMessage`) if you don't check their ban state before rendering them. These API calls will fail on the server, causing a poor UX and support tickets.
@@ -324,16 +334,49 @@ Require chat shape (1:1, group, community), channel source (never hardcode chann
324
334
  - Use `community` or `live` for groups.
325
335
  If the type intentionally mismatches the UI name, add `// vise: channel type rationale — <reason>` to the code.
326
336
 
327
- ## Monorepos
337
+ For communities:
328
338
 
329
- If `vise inspect .` reports multiple surfaces, choose the intended app with the user or surrounding task context. Pass it consistently:
339
+ ```bash
340
+ vise search-docs "community create join leave members invitation"
341
+ vise plan . --request "<community request>"
342
+ ```
343
+
344
+ Require the community surface (create, browse/join, detail + members, or membership management), the privacy model (public instant-join vs private join-request approval — they differ), and a concrete community ID source (never hardcode a communityId). Query communities and members through a Live Collection, handle the join-request flow for private communities, gate moderator actions (roles/ban) by role, and clean up observers. `vise plan` returns the community `completenessChecklist`; opt out of anything out of scope with `// vise: scope-omit <id> <reason>`.
345
+
346
+ For the social graph (follow/followers):
330
347
 
331
348
  ```bash
332
- vise plan . --surface apps/web --request "<user intent>"
333
- vise validate . --surface apps/web
334
- vise run-sensors . --surface apps/web
349
+ vise search-docs "follow unfollow followers following block user"
350
+ vise plan . --request "<follow/social-graph request>"
335
351
  ```
336
352
 
353
+ Require the surface (follow button, follower list, following list, or blocked users), the follow model (automatic vs follow-request approval — they differ), and a concrete target userId source (never invent a userId). Query follower/following lists through a Live Collection; handle the follow-request pending/accept/decline flow when following isn't automatic; clean up observers.
354
+
355
+ For an in-app notification tray (distinct from push setup — that's a push/FCM/APNS concern):
356
+
357
+ ```bash
358
+ vise search-docs "in-app notification tray seen settings"
359
+ vise plan . --request "<notification tray request>"
360
+ ```
361
+
362
+ Observe the notification tray as a Live Object/Collection, mark items/tray seen so the unseen badge clears server-side, respect Amity's server-side notification settings, and clean up the observer.
363
+
364
+ ## Design-Conformant UI (Design Contract)
365
+
366
+ The customer wants social.plus UI that looks like *their* app, not a generic kit. Match their aesthetic from whatever design signal exists, in this priority:
367
+
368
+ 1. **Customer-provided prototype (HTML/CSS).** If the customer gives you an HTML/CSS prototype (a static mockup, an existing page, a design export), run `vise design extract <prototypePath> --repo .` first. It writes `sp-vise/design-contract.json` — a *graded* contract: **declared** tokens (CSS custom properties — exact, authoritative) and **inferred** tokens (repeated literal values — advisory). `vise plan` then surfaces this contract in its `designContract` field. Build the feed using the declared tokens; treat inferred tokens as advisory and confirm brand values with the customer when the contract `strength` is `weak`.
369
+ 2. **Host app's own design system.** With no external prototype, run `vise design extract --from-project .` to derive the contract from the project's *own* design system — CSS custom properties (incl. shadcn `:root` and Tailwind v4 `@theme`), TS/JS token modules (`theme.ts`/`tokens.ts`), inline tailwind configs, and native sources (Android `colors.xml`/`dimens.xml`, Flutter `Color(0x…)`, iOS `.xcassets/*.colorset` + Swift colors). Then build the new surface from those tokens (also enforced by `*.design.reuse-detected-tokens`). `vise inspect` lists the detected design signals.
370
+ 3. **Neither.** Use a neutral, accessible default and **state in your summary that no design source was found** — do not invent brand colors, spacing, or a design system. Ask the customer for a prototype, their theme file, or screenshots.
371
+
372
+ To let the customer (or yourself) **see** the contract and check the match, run `vise design preview .` — it writes a self-contained `sp-vise/design-preview.html` with the tokens as visual swatches, the conformance report, and the HTML prototype embedded (`--reference <prototype>`) for side-by-side review. It's advisory: it renders the artifact for a human/VLM to judge, and is not an automated pixel diff (a true render diff belongs to your own browser tooling, not Vise).
373
+
374
+ The design contract is **advisory generation guidance**: it shapes what you build, but it is not a deterministic gate and never fails `vise check`. A token-poor prototype yields a weaker — never wrong — contract. When you record a design attestation, cite the contract `digest` so the feed can be claimed conformant to the prototype.
375
+
376
+ ## Monorepos
377
+
378
+ If `vise inspect .` reports multiple surfaces, pick the intended app and pass `--surface <path>` consistently to `plan`/`validate`/`run-sensors`. Details: `reference/operations.md`.
379
+
337
380
  ## Final Response Discipline
338
381
 
339
382
  Report:
@@ -0,0 +1,39 @@
1
+ # Debugging & Troubleshooting (Day 2 Operations)
2
+
3
+ Load this when the customer reports an SDK-specific runtime crash, build failure, or logical error (e.g. data not loading).
4
+
5
+ Start with `vise debug` before broad repo inspection or sidecar exploration.
6
+
7
+ **CRITICAL SCOPE LIMIT:** Do NOT spam `vise debug` for generic UI/CSS errors or non-SDK issues. ONLY call `vise debug` if the error explicitly contains keywords like `social.plus`, `social-plus`, `Amity`, `live-collection`, or if the error occurs in a file that imports `social.plus` packages.
8
+
9
+ For the first repair pass, prefer the compact troubleshooting loop:
10
+
11
+ ```bash
12
+ vise debug . --error-file "logs/crash.log" --brief
13
+ vise check . --ci
14
+ vise run-sensors .
15
+ ```
16
+
17
+ When debugging, pass the stack trace to Vise:
18
+
19
+ ```bash
20
+ vise debug . --error "stack trace or exception message here"
21
+ ```
22
+ Or, if the error is in a file:
23
+ ```bash
24
+ vise debug . --error-file "logs/crash.log"
25
+ ```
26
+ Or, for a compact repair brief:
27
+ ```bash
28
+ vise debug . --error-file "logs/crash.log" --brief
29
+ ```
30
+
31
+ The output contains `likelyCause`, `correlatedRules`, and a compact `repairBrief` with:
32
+
33
+ - the primary rule to repair first
34
+ - likely file hints
35
+ - minimum patch shape
36
+ - invariants to preserve
37
+ - verification commands
38
+
39
+ Do not guess the fix before consulting `vise debug`. Do not read broad `sp-vise/` history on the first pass unless the `repairBrief` is ambiguous or the first repair fails.
@@ -0,0 +1,59 @@
1
+ # Operations: Attestation, Engagement Scope, Monorepos
2
+
3
+ Load this when resolving `vise check` findings, working with engagement scope, or operating in a monorepo.
4
+
5
+ ## Attestation Flow
6
+
7
+ `vise check` reports each applicable rule as one of `deterministic-pass`, `attestation-needed`, `failed`, or `blocked`. `attestation-needed` and `failed` are NOT warnings to skip — they block "I'm done." Resolve them in one of two ways:
8
+
9
+ 1. **Make the deterministic check fire (preferred).** Re-read `vise explain <rule-id>` for the rule's enforcement details, adjust the code so the check passes, then run `vise check` again. After it goes green, run `vise sync` to persist the deterministic-pass attestation files.
10
+ 2. **Record an attestation with evidence.** When the rule cannot fire because the customer's architecture is unconventional (DI wrappers, custom abstractions, etc.), use `vise attest`:
11
+
12
+ ```bash
13
+ vise attest . \
14
+ --rule typescript.client.region \
15
+ --confidence high \
16
+ --signer host-agent \
17
+ --rationale "API_REGIONS.EU is explicitly passed as the second argument to Client.createClient in src/socialSDK.ts." \
18
+ --evidence-file ./.tmp/evidence.json
19
+ ```
20
+
21
+ The evidence file is a JSON object matching the `evidence_required` schema for that rule (`vise explain <rule-id>` shows the required fields). Include concrete source file paths in evidence values whenever the evidence depends on code, for example `{ "region_source": "src/socialSDK.ts" }` or `{ "login_flow": "src/auth/socialSession.ts" }`. `vise attest` fingerprints cited files, and future `vise check` runs mark the attestation stale if those files change.
22
+
23
+ ### Confidence honesty
24
+
25
+ - `high` — direct evidence the deterministic check would have fired if it had been more flexible. Auto-recorded.
26
+ - `medium` — reasoned about it but cannot point to a single unambiguous location. May require human signer depending on rule.
27
+ - `low` — guess. Always requires `--signer human`.
28
+
29
+ If the rule's `escalation_threshold` is above your confidence, `vise attest --signer host-agent` is rejected. In v0.4 there is no hosted review queue yet, so the path is: stop, surface to the user with the rule id and the evidence you have, and ask them to run `vise attest --signer human --identity <their name or email>` themselves.
30
+
31
+ ### What `vise sync` is NOT
32
+
33
+ - `vise sync` does **not** accept `--attest` or any flag. It only persists deterministic-pass results.
34
+ - Passing unknown flags to `vise sync` will error in v0.4 — don't try to use it as a shortcut for attestation.
35
+ - To record an attestation, use `vise attest`. Always.
36
+
37
+ ### Treat warnings as blockers
38
+
39
+ If a legacy `vise validate` run reports `needs-review` with warnings, those warnings correspond to compliance rules in `attestation-needed` state. Do not interpret them as "code is fine, just static-analysis noise." Run `vise check` to get the structured rule list, then resolve each one as above.
40
+
41
+ ## Engagement Scope (Optional)
42
+
43
+ If the customer has set up `sp-vise/engagement.json` (via `vise engagement init`), respect the declared scope. `vise engagement show .` prints the engagement metadata: tier, customer id, contracted outcomes, reviewer assignment.
44
+
45
+ When `vise init` reports a `warnings` array containing "not in the engagement scope", surface that to the user — don't silently widen scope. The user either extends the scope by editing `engagement.json` or accepts that the current work is out-of-contract.
46
+
47
+ If no `engagement.json` exists, do not create one without the user's intent. `vise engagement init` is a contractual artifact; agents should only run it when the user explicitly asks to set up engagement metadata, typically with `--tier`, `--customer-id`, and `--scope`.
48
+
49
+ If `vise` is not installed, use `npm install -g @amityco/social-plus-vise`, then retry the `vise` command.
50
+
51
+ ## Monorepos
52
+
53
+ If `vise inspect .` reports multiple surfaces, choose the intended app with the user or surrounding task context. Pass it consistently:
54
+
55
+ ```bash
56
+ vise plan . --surface apps/web --request "<user intent>"
57
+ vise validate . --surface apps/web
58
+ vise run-sensors . --surface apps/web
59
+ ```
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: vise-harness-engineer
3
+ description: "Instructions for authoring new compliance rules in social.plus Vise."
4
+ ---
5
+
6
+ # Vise Harness Engineer Instructions
7
+
8
+ When asked by the user to add a new compliance rule (e.g., "Add a rule for post impressions" or "Integrators misconfigure X"), follow these steps:
9
+
10
+ ## 1. Context Gathering
11
+ You MUST verify that the feature exists and understand its implementation before authoring a rule.
12
+ - Read `social-plus-docs/llms.txt` to verify feature support and understand its SDK surface.
13
+ - Inspect the relevant SDK codebase (e.g., `AmityTypescriptSDK`) to see how the feature is implemented.
14
+ - Check `social-plus-foundry/rules/` to find the correct YAML file to modify (e.g., `feed.yaml` or `auth.yaml`).
15
+
16
+ ## 2. Rule Authoring
17
+ Add a new YAML block to the appropriate rule file following this schema:
18
+ - `id`: A unique string identifying the rule (e.g., `react.feed.impressions`).
19
+ - `title`: A human-readable title.
20
+ - `severity`: `warning` or `error`.
21
+ - `rationale`: Why this rule exists.
22
+ - `feedforward`: IMPORTANT! Proactive instructions that will be injected into the integrator's plan to prevent the bug. Use strong language like "You MUST...".
23
+ - `enforcement.inferential`: Provide a precise prompt for the integrator's host agent to review the code.
24
+ ```yaml
25
+ enforcement:
26
+ inferential:
27
+ prompt: "Review the Feed component. Does it use IntersectionObserver to fire trackImpression?"
28
+ target_signer: "host-agent"
29
+ ```
30
+
31
+ ## 3. Do Not Create deterministic Sensors Unless Trivial
32
+ Do not write complex AST deterministic sensors (`validator-finding-absent`) unless explicitly requested. The inferential sensor is the preferred fallback for complex logic.
33
+
34
+ ## 4. No capabilities.yaml Modifications
35
+ The capability matrix is fetched dynamically. Do not attempt to update any `capabilities.yaml` file.
Binary file