@amityco/social-plus-vise 0.11.0 → 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.
- package/CHANGELOG.md +58 -0
- package/README.md +32 -1
- package/dist/capabilities.js +447 -0
- package/dist/outcomes.js +444 -2
- package/dist/server.js +76 -3
- package/dist/tools/ast.js +25 -0
- package/dist/tools/compliance.js +20 -0
- package/dist/tools/design.js +1496 -0
- package/dist/tools/docs.js +9 -4
- package/dist/tools/integration.js +83 -7
- package/dist/tools/project.js +652 -44
- package/dist/tools/sdkVersion.js +129 -0
- package/package.json +16 -3
- package/rules/comments.yaml +0 -72
- package/rules/feed.yaml +1150 -11
- package/rules/sdk-lifecycle.yaml +9 -9
- package/skills/social-plus-vise/SKILL.md +93 -93
- package/skills/social-plus-vise/reference/debugging.md +39 -0
- package/skills/social-plus-vise/reference/operations.md +59 -0
package/rules/sdk-lifecycle.yaml
CHANGED
|
@@ -963,7 +963,7 @@
|
|
|
963
963
|
{
|
|
964
964
|
"id": "android.session.renewal",
|
|
965
965
|
"version": 1,
|
|
966
|
-
"title": "Android login must pass
|
|
966
|
+
"title": "Android login must pass a SessionHandler that renews tokens",
|
|
967
967
|
"severity": "warning",
|
|
968
968
|
"rationale": "Access tokens expire in production. Without a session handler that calls renewal.renew() in sessionWillRenewAccessToken, the SDK cannot refresh sessions and customers see silent auth failures.",
|
|
969
969
|
"applies_when": {
|
|
@@ -992,7 +992,7 @@
|
|
|
992
992
|
"evidence_required": [
|
|
993
993
|
{
|
|
994
994
|
"field": "session_handler",
|
|
995
|
-
"description": "Where
|
|
995
|
+
"description": "Where the SessionHandler.sessionWillRenewAccessToken implementation lives.",
|
|
996
996
|
"upload_policy": "upload-with-consent"
|
|
997
997
|
}
|
|
998
998
|
]
|
|
@@ -1007,7 +1007,7 @@
|
|
|
1007
1007
|
{
|
|
1008
1008
|
"id": "flutter.session.renewal",
|
|
1009
1009
|
"version": 1,
|
|
1010
|
-
"title": "Flutter login must pass
|
|
1010
|
+
"title": "Flutter login must pass a session handler callback (AccessTokenRenewal renewal) that renews tokens",
|
|
1011
1011
|
"severity": "warning",
|
|
1012
1012
|
"rationale": "Access tokens expire in production. Without a session handler that calls renewal.renew() in sessionWillRenewAccessToken, the SDK cannot refresh sessions and customers see silent auth failures.",
|
|
1013
1013
|
"applies_when": {
|
|
@@ -1051,9 +1051,9 @@
|
|
|
1051
1051
|
{
|
|
1052
1052
|
"id": "ios.session.renewal",
|
|
1053
1053
|
"version": 1,
|
|
1054
|
-
"title": "iOS login must pass
|
|
1054
|
+
"title": "iOS login must pass a SessionHandler that renews tokens",
|
|
1055
1055
|
"severity": "warning",
|
|
1056
|
-
"rationale": "Access tokens expire in production. Without
|
|
1056
|
+
"rationale": "Access tokens expire in production. Without a SessionHandler that calls renewal.renew() in sessionWillRenewAccessToken, the iOS SDK cannot refresh sessions and customers see silent auth failures.",
|
|
1057
1057
|
"applies_when": {
|
|
1058
1058
|
"platforms": [
|
|
1059
1059
|
"ios"
|
|
@@ -1080,7 +1080,7 @@
|
|
|
1080
1080
|
"evidence_required": [
|
|
1081
1081
|
{
|
|
1082
1082
|
"field": "session_handler",
|
|
1083
|
-
"description": "Where
|
|
1083
|
+
"description": "Where the SessionHandler.sessionWillRenewAccessToken implementation lives.",
|
|
1084
1084
|
"upload_policy": "upload-with-consent"
|
|
1085
1085
|
}
|
|
1086
1086
|
]
|
|
@@ -1380,7 +1380,7 @@
|
|
|
1380
1380
|
"version": 1,
|
|
1381
1381
|
"title": "Android session handler must be retained",
|
|
1382
1382
|
"severity": "warning",
|
|
1383
|
-
"rationale": "
|
|
1383
|
+
"rationale": "A SessionHandler declared as a function-local variable gets garbage collected. The SDK's renewal callback will then call into nothing, causing sessions to silently expire.",
|
|
1384
1384
|
"applies_when": {
|
|
1385
1385
|
"platforms": [
|
|
1386
1386
|
"android"
|
|
@@ -1416,7 +1416,7 @@
|
|
|
1416
1416
|
"version": 1,
|
|
1417
1417
|
"title": "Flutter session handler must be retained",
|
|
1418
1418
|
"severity": "warning",
|
|
1419
|
-
"rationale": "
|
|
1419
|
+
"rationale": "A session handler callback held only as a function-local variable gets garbage collected by Dart. The SDK's renewal callback will then call into nothing, causing sessions to silently expire.",
|
|
1420
1420
|
"applies_when": {
|
|
1421
1421
|
"platforms": [
|
|
1422
1422
|
"flutter"
|
|
@@ -1452,7 +1452,7 @@
|
|
|
1452
1452
|
"version": 1,
|
|
1453
1453
|
"title": "iOS session handler must be retained",
|
|
1454
1454
|
"severity": "warning",
|
|
1455
|
-
"rationale": "
|
|
1455
|
+
"rationale": "A SessionHandler declared as a function-local variable will be released by ARC. The SDK's renewal callback will then call into nothing, causing sessions to silently expire.",
|
|
1456
1456
|
"applies_when": {
|
|
1457
1457
|
"platforms": [
|
|
1458
1458
|
"ios"
|
|
@@ -35,63 +35,17 @@ 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
|
-
## Debugging
|
|
43
|
-
|
|
44
|
-
If the customer reports an SDK-specific runtime crash, build failure, or logical error (e.g. data not loading), start with `vise debug` before broad repo inspection or sidecar exploration.
|
|
45
|
-
|
|
46
|
-
**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.
|
|
47
|
-
|
|
48
|
-
For the first repair pass, prefer the compact troubleshooting loop:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
vise debug . --error-file "logs/crash.log" --brief
|
|
52
|
-
vise check . --ci
|
|
53
|
-
vise run-sensors .
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
When debugging, pass the stack trace to Vise:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
vise debug . --error "stack trace or exception message here"
|
|
60
|
-
```
|
|
61
|
-
Or, if the error is in a file:
|
|
62
|
-
```bash
|
|
63
|
-
vise debug . --error-file "logs/crash.log"
|
|
64
|
-
```
|
|
65
|
-
Or, for a compact repair brief:
|
|
66
|
-
```bash
|
|
67
|
-
vise debug . --error-file "logs/crash.log" --brief
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
The output contains `likelyCause`, `correlatedRules`, and a compact `repairBrief` with:
|
|
44
|
+
## Debugging (Day 2 Operations)
|
|
71
45
|
|
|
72
|
-
- the
|
|
73
|
-
- likely file hints
|
|
74
|
-
- minimum patch shape
|
|
75
|
-
- invariants to preserve
|
|
76
|
-
- verification commands
|
|
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`**.
|
|
77
47
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## Engagement Scope (Optional)
|
|
81
|
-
|
|
82
|
-
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.
|
|
83
|
-
|
|
84
|
-
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.
|
|
85
|
-
|
|
86
|
-
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`.
|
|
87
|
-
|
|
88
|
-
If `vise` is not installed, use:
|
|
89
|
-
|
|
90
|
-
```sh
|
|
91
|
-
npm install -g @amityco/social-plus-vise
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
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`.)
|
|
95
49
|
|
|
96
50
|
## Stop Instead Of Guessing
|
|
97
51
|
|
|
@@ -175,6 +129,15 @@ Amity exposes a global `unreadCount` stream that syncs across devices.
|
|
|
175
129
|
- **Correct pattern**: Subscribe to the unread stream provided by the SDK (e.g. `AmityCoreClient.unreadCount()`, `getUnreadCount()`, `unread.stream`).
|
|
176
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.
|
|
177
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
|
+
|
|
178
141
|
### File Upload & Media
|
|
179
142
|
|
|
180
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.
|
|
@@ -202,15 +165,36 @@ When using a LiveCollection or live object, also handle connection-state changes
|
|
|
202
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.
|
|
203
166
|
- **Correct pattern:** Apply `.includeDeleted(false)`, `.statuses([.published])`, or `.feedType(AmityFeedType.PUBLISHED)` to your query builders.
|
|
204
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
|
+
|
|
205
173
|
### Opaque Pagination Cursors
|
|
206
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.
|
|
207
175
|
- **Correct pattern:** Pass the exact `nextPageToken` returned from the previous query result, or call `.loadMore()` / `.nextPage()` on the collection itself.
|
|
208
176
|
- **Wrong pattern:** `.nextPage(pageNumber * 20)`
|
|
209
177
|
|
|
210
178
|
### Parent-Child Post Rendering
|
|
211
|
-
Amity
|
|
212
|
-
- **Correct pattern:**
|
|
213
|
-
- **Wrong pattern:** `Text(post.data.text)` with no
|
|
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`.
|
|
214
198
|
|
|
215
199
|
### Explicit Feed Target Types
|
|
216
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.
|
|
@@ -239,43 +223,13 @@ Amity routes custom-shape post payloads via the `dataType` tag. Omitting it make
|
|
|
239
223
|
|
|
240
224
|
## SDK Version Handling
|
|
241
225
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
## Attestation Flow
|
|
245
|
-
|
|
246
|
-
`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:
|
|
247
|
-
|
|
248
|
-
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.
|
|
249
|
-
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`:
|
|
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**.
|
|
250
227
|
|
|
251
|
-
|
|
252
|
-
vise attest . \
|
|
253
|
-
--rule typescript.client.region \
|
|
254
|
-
--confidence high \
|
|
255
|
-
--signer host-agent \
|
|
256
|
-
--rationale "API_REGIONS.EU is explicitly passed as the second argument to Client.createClient in src/socialSDK.ts." \
|
|
257
|
-
--evidence-file ./.tmp/evidence.json
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
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.
|
|
261
|
-
|
|
262
|
-
### Confidence honesty
|
|
263
|
-
|
|
264
|
-
- `high` — direct evidence the deterministic check would have fired if it had been more flexible. Auto-recorded.
|
|
265
|
-
- `medium` — reasoned about it but cannot point to a single unambiguous location. May require human signer depending on rule.
|
|
266
|
-
- `low` — guess. Always requires `--signer human`.
|
|
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.
|
|
267
229
|
|
|
268
|
-
|
|
230
|
+
## Resolving `vise check` Findings
|
|
269
231
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
- `vise sync` does **not** accept `--attest` or any flag. It only persists deterministic-pass results.
|
|
273
|
-
- Passing unknown flags to `vise sync` will error in v0.4 — don't try to use it as a shortcut for attestation.
|
|
274
|
-
- To record an attestation, use `vise attest`. Always.
|
|
275
|
-
|
|
276
|
-
### Treat warnings as blockers
|
|
277
|
-
|
|
278
|
-
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`**.
|
|
279
233
|
|
|
280
234
|
## Feature Guidance
|
|
281
235
|
|
|
@@ -288,6 +242,14 @@ vise plan . --request "<feed or post request>"
|
|
|
288
242
|
|
|
289
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.
|
|
290
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
|
+
|
|
291
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:
|
|
292
254
|
|
|
293
255
|
- Flutter: `const String.fromEnvironment('AMITY_COMMUNITY_ID')` — value injected via `flutter run --dart-define=AMITY_COMMUNITY_ID=…`
|
|
@@ -336,6 +298,11 @@ When rendering moderation actions (like `flagPost`, `deletePost`, `banUser`, `mu
|
|
|
336
298
|
|
|
337
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.
|
|
338
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
|
+
|
|
339
306
|
### User Ban State
|
|
340
307
|
|
|
341
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.
|
|
@@ -367,16 +334,49 @@ Require chat shape (1:1, group, community), channel source (never hardcode chann
|
|
|
367
334
|
- Use `community` or `live` for groups.
|
|
368
335
|
If the type intentionally mismatches the UI name, add `// vise: channel type rationale — <reason>` to the code.
|
|
369
336
|
|
|
370
|
-
|
|
337
|
+
For communities:
|
|
371
338
|
|
|
372
|
-
|
|
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):
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
vise search-docs "follow unfollow followers following block user"
|
|
350
|
+
vise plan . --request "<follow/social-graph request>"
|
|
351
|
+
```
|
|
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):
|
|
373
356
|
|
|
374
357
|
```bash
|
|
375
|
-
vise
|
|
376
|
-
vise
|
|
377
|
-
vise run-sensors . --surface apps/web
|
|
358
|
+
vise search-docs "in-app notification tray seen settings"
|
|
359
|
+
vise plan . --request "<notification tray request>"
|
|
378
360
|
```
|
|
379
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
|
+
|
|
380
380
|
## Final Response Discipline
|
|
381
381
|
|
|
382
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
|
+
```
|