@amityco/social-plus-vise 1.4.6 → 1.5.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/CHANGELOG.md +26 -10
- package/README.md +59 -31
- package/dist/capabilities.js +1 -1
- package/dist/flow.js +2 -2
- package/dist/humanFormat.js +2 -0
- package/dist/outcomes.js +24 -10
- package/dist/server.js +421 -62
- package/dist/tools/compliance.js +318 -12
- package/dist/tools/debug.js +16 -1
- package/dist/tools/design.js +47 -16
- package/dist/tools/integration.js +40 -7
- package/dist/tools/project.js +52 -10
- package/dist/tools/smoke.js +169 -1
- package/docs/vise-how-it-helps.html +574 -105
- package/package.json +1 -1
- package/rules/feed.yaml +37 -15
- package/rules/live-data.yaml +10 -5
- package/rules/sdk-lifecycle.yaml +23 -0
- package/skills/social-plus-vise/SKILL.md +33 -13
package/package.json
CHANGED
package/rules/feed.yaml
CHANGED
|
@@ -68,6 +68,28 @@
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
"id": "ios.feed.source-used",
|
|
73
|
+
"version": 1,
|
|
74
|
+
"title": "iOS feed integration must use SDK post APIs",
|
|
75
|
+
"severity": "warning",
|
|
76
|
+
"rationale": "A community/profile-only SDK integration is not an add-feed implementation. Feed surfaces must query or create SDK posts and render SDK post data.",
|
|
77
|
+
"applies_when": {
|
|
78
|
+
"platforms": ["ios"],
|
|
79
|
+
"outcomes": ["add-feed", "validate-setup"]
|
|
80
|
+
},
|
|
81
|
+
"enforcement": {
|
|
82
|
+
"deterministic": [
|
|
83
|
+
{ "check": "validator-finding-absent", "finding_rule_id": "ios.feed.source-used" }
|
|
84
|
+
],
|
|
85
|
+
"attestation": {
|
|
86
|
+
"allowed": false,
|
|
87
|
+
"host_agent_min_confidence": "high",
|
|
88
|
+
"human_allowed": false,
|
|
89
|
+
"evidence_required": []
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
71
93
|
{
|
|
72
94
|
"id": "typescript.feed.target.literal",
|
|
73
95
|
"version": 1,
|
|
@@ -1547,9 +1569,9 @@
|
|
|
1547
1569
|
"id": "typescript.feed.target-type-explicit",
|
|
1548
1570
|
"version": 1,
|
|
1549
1571
|
"symbol_anchored": {"platform":"typescript","basis":"pattern"},
|
|
1550
|
-
"title": "
|
|
1572
|
+
"title": "TypeScript createPost targetType must be a valid, bound write target",
|
|
1551
1573
|
"severity": "warning",
|
|
1552
|
-
"rationale": "TypeScript
|
|
1574
|
+
"rationale": "TypeScript createPost targets must resolve to a valid SDK write target (user or community) with a targetId from props/state/auth/route, never a global aggregate feed target.",
|
|
1553
1575
|
"applies_when": {
|
|
1554
1576
|
"platforms": [
|
|
1555
1577
|
"typescript"
|
|
@@ -1573,7 +1595,7 @@
|
|
|
1573
1595
|
"evidence_required": [
|
|
1574
1596
|
{
|
|
1575
1597
|
"field": "target_type_rationale",
|
|
1576
|
-
"description": "Reason why this composer
|
|
1598
|
+
"description": "Reason why this composer uses this targetType/targetId binding.",
|
|
1577
1599
|
"upload_policy": "upload-with-consent"
|
|
1578
1600
|
}
|
|
1579
1601
|
]
|
|
@@ -1584,9 +1606,9 @@
|
|
|
1584
1606
|
"id": "react-native.feed.target-type-explicit",
|
|
1585
1607
|
"version": 1,
|
|
1586
1608
|
"symbol_anchored": {"platform":"react-native","basis":"pattern"},
|
|
1587
|
-
"title": "
|
|
1609
|
+
"title": "React Native createPost targetType must be a valid, bound write target",
|
|
1588
1610
|
"severity": "warning",
|
|
1589
|
-
"rationale": "React Native
|
|
1611
|
+
"rationale": "React Native createPost targets must resolve to a valid SDK write target (user or community) with a targetId from props/state/auth/route, never a global aggregate feed target.",
|
|
1590
1612
|
"applies_when": {
|
|
1591
1613
|
"platforms": [
|
|
1592
1614
|
"react-native"
|
|
@@ -1610,7 +1632,7 @@
|
|
|
1610
1632
|
"evidence_required": [
|
|
1611
1633
|
{
|
|
1612
1634
|
"field": "target_type_rationale",
|
|
1613
|
-
"description": "Reason why this composer
|
|
1635
|
+
"description": "Reason why this composer uses this targetType/targetId binding.",
|
|
1614
1636
|
"upload_policy": "upload-with-consent"
|
|
1615
1637
|
}
|
|
1616
1638
|
]
|
|
@@ -1621,9 +1643,9 @@
|
|
|
1621
1643
|
"id": "android.feed.target-type-explicit",
|
|
1622
1644
|
"version": 1,
|
|
1623
1645
|
"symbol_anchored": {"platform":"android","basis":"pattern"},
|
|
1624
|
-
"title": "
|
|
1646
|
+
"title": "Android createPost targetType must be a valid, bound write target",
|
|
1625
1647
|
"severity": "warning",
|
|
1626
|
-
"rationale": "Android
|
|
1648
|
+
"rationale": "Android createPost targets must resolve to a valid SDK write target (user or community) with a targetId from Intent extras/ViewModel/auth/route state, never a global aggregate feed target.",
|
|
1627
1649
|
"applies_when": {
|
|
1628
1650
|
"platforms": [
|
|
1629
1651
|
"android"
|
|
@@ -1647,7 +1669,7 @@
|
|
|
1647
1669
|
"evidence_required": [
|
|
1648
1670
|
{
|
|
1649
1671
|
"field": "target_type_rationale",
|
|
1650
|
-
"description": "Reason why this composer
|
|
1672
|
+
"description": "Reason why this composer uses this targetType/targetId binding.",
|
|
1651
1673
|
"upload_policy": "upload-with-consent"
|
|
1652
1674
|
}
|
|
1653
1675
|
]
|
|
@@ -1658,9 +1680,9 @@
|
|
|
1658
1680
|
"id": "flutter.feed.target-type-explicit",
|
|
1659
1681
|
"version": 1,
|
|
1660
1682
|
"symbol_anchored": {"platform":"flutter","types":["AmityPostTargetType"],"basis":"names-only"},
|
|
1661
|
-
"title": "
|
|
1683
|
+
"title": "Flutter createPost targetType must be a valid, bound write target",
|
|
1662
1684
|
"severity": "warning",
|
|
1663
|
-
"rationale": "Flutter
|
|
1685
|
+
"rationale": "Flutter createPost targets must resolve to a valid SDK write target (user or community) with a targetId from Widget/auth/route state, never a global aggregate feed target.",
|
|
1664
1686
|
"applies_when": {
|
|
1665
1687
|
"platforms": [
|
|
1666
1688
|
"flutter"
|
|
@@ -1684,7 +1706,7 @@
|
|
|
1684
1706
|
"evidence_required": [
|
|
1685
1707
|
{
|
|
1686
1708
|
"field": "target_type_rationale",
|
|
1687
|
-
"description": "Reason why this composer
|
|
1709
|
+
"description": "Reason why this composer uses this targetType/targetId binding.",
|
|
1688
1710
|
"upload_policy": "upload-with-consent"
|
|
1689
1711
|
}
|
|
1690
1712
|
]
|
|
@@ -1695,9 +1717,9 @@
|
|
|
1695
1717
|
"id": "ios.feed.target-type-explicit",
|
|
1696
1718
|
"version": 1,
|
|
1697
1719
|
"symbol_anchored": {"platform":"ios","types":["AmityPostTargetType"],"basis":"names-only"},
|
|
1698
|
-
"title": "
|
|
1720
|
+
"title": "iOS createPost targetType must be a valid, bound write target",
|
|
1699
1721
|
"severity": "warning",
|
|
1700
|
-
"rationale": "iOS
|
|
1722
|
+
"rationale": "iOS createPost targets must resolve to a valid SDK write target (user or community) with a targetId from view/auth/route state, never a global aggregate feed target.",
|
|
1701
1723
|
"applies_when": {
|
|
1702
1724
|
"platforms": [
|
|
1703
1725
|
"ios"
|
|
@@ -1721,7 +1743,7 @@
|
|
|
1721
1743
|
"evidence_required": [
|
|
1722
1744
|
{
|
|
1723
1745
|
"field": "target_type_rationale",
|
|
1724
|
-
"description": "Reason why this composer
|
|
1746
|
+
"description": "Reason why this composer uses this targetType/targetId binding.",
|
|
1725
1747
|
"upload_policy": "upload-with-consent"
|
|
1726
1748
|
}
|
|
1727
1749
|
]
|
package/rules/live-data.yaml
CHANGED
|
@@ -524,7 +524,8 @@
|
|
|
524
524
|
"human_allowed": false,
|
|
525
525
|
"evidence_required": []
|
|
526
526
|
}
|
|
527
|
-
}
|
|
527
|
+
},
|
|
528
|
+
"symptoms": ["notLoggedIn", "NOT_LOGGED_IN", "session is not established", "session not ready", "user is not logged in", "SessionState"]
|
|
528
529
|
},
|
|
529
530
|
{
|
|
530
531
|
"id": "flutter.live-collection.session-ready-requery",
|
|
@@ -546,7 +547,8 @@
|
|
|
546
547
|
"human_allowed": false,
|
|
547
548
|
"evidence_required": []
|
|
548
549
|
}
|
|
549
|
-
}
|
|
550
|
+
},
|
|
551
|
+
"symptoms": ["notLoggedIn", "NOT_LOGGED_IN", "session is not established", "session not ready", "user is not logged in", "SessionState"]
|
|
550
552
|
},
|
|
551
553
|
{
|
|
552
554
|
"id": "ios.live-collection.session-ready-requery",
|
|
@@ -568,7 +570,8 @@
|
|
|
568
570
|
"human_allowed": false,
|
|
569
571
|
"evidence_required": []
|
|
570
572
|
}
|
|
571
|
-
}
|
|
573
|
+
},
|
|
574
|
+
"symptoms": ["notLoggedIn", "NOT_LOGGED_IN", "session is not established", "session not ready", "user is not logged in", "SessionState"]
|
|
572
575
|
},
|
|
573
576
|
{
|
|
574
577
|
"id": "typescript.live-collection.session-ready-requery",
|
|
@@ -590,7 +593,8 @@
|
|
|
590
593
|
"human_allowed": false,
|
|
591
594
|
"evidence_required": []
|
|
592
595
|
}
|
|
593
|
-
}
|
|
596
|
+
},
|
|
597
|
+
"symptoms": ["notLoggedIn", "NOT_LOGGED_IN", "session is not established", "session not ready", "user is not logged in", "SessionState"]
|
|
594
598
|
},
|
|
595
599
|
{
|
|
596
600
|
"id": "react-native.live-collection.session-ready-requery",
|
|
@@ -612,7 +616,8 @@
|
|
|
612
616
|
"human_allowed": false,
|
|
613
617
|
"evidence_required": []
|
|
614
618
|
}
|
|
615
|
-
}
|
|
619
|
+
},
|
|
620
|
+
"symptoms": ["notLoggedIn", "NOT_LOGGED_IN", "session is not established", "session not ready", "user is not logged in", "SessionState"]
|
|
616
621
|
}
|
|
617
622
|
]
|
|
618
623
|
}
|
package/rules/sdk-lifecycle.yaml
CHANGED
|
@@ -2323,6 +2323,29 @@
|
|
|
2323
2323
|
},
|
|
2324
2324
|
"symptoms": ["Dependency-only integration", "Static social shell", "No SDK-backed runtime data"]
|
|
2325
2325
|
},
|
|
2326
|
+
{
|
|
2327
|
+
"id": "ios.sdk.source-used",
|
|
2328
|
+
"version": 1,
|
|
2329
|
+
"title": "iOS SDK dependency must be used by product source",
|
|
2330
|
+
"severity": "warning",
|
|
2331
|
+
"rationale": "Installing the iOS social.plus SDK is not an integration. Product Swift source must use the SDK or UIKit manager beyond static initialization: login/register a runtime user and query or create social.plus data.",
|
|
2332
|
+
"applies_when": {
|
|
2333
|
+
"platforms": ["ios"],
|
|
2334
|
+
"outcomes": ["setup-sdk", "setup-live-data", "add-feed", "add-comments", "add-chat", "add-community", "add-follow", "validate-setup"]
|
|
2335
|
+
},
|
|
2336
|
+
"enforcement": {
|
|
2337
|
+
"deterministic": [
|
|
2338
|
+
{ "check": "validator-finding-absent", "finding_rule_id": "ios.sdk.source-used" }
|
|
2339
|
+
],
|
|
2340
|
+
"attestation": {
|
|
2341
|
+
"allowed": false,
|
|
2342
|
+
"host_agent_min_confidence": "high",
|
|
2343
|
+
"human_allowed": false,
|
|
2344
|
+
"evidence_required": []
|
|
2345
|
+
}
|
|
2346
|
+
},
|
|
2347
|
+
"symptoms": ["Dependency-only integration", "Static social shell", "No SDK-backed runtime data"]
|
|
2348
|
+
},
|
|
2326
2349
|
{
|
|
2327
2350
|
"id": "react-native.ui.runtime-probe-copy",
|
|
2328
2351
|
"version": 1,
|
|
@@ -19,9 +19,11 @@ vise plan . --request "<user intent>"
|
|
|
19
19
|
vise init . --request "<user intent>"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
**This 3-step order is the single-surface path.** For a **broad / multi-surface / experience** request, `vise init` will *refuse* until the blueprint is signed off (exit 7) — so the real order there is `inspect → plan → creative → creative accept → workplan next` (sign the blueprint) `→ init` per surface. Do not treat the exit-7 refusal from `vise init` as an error: it means "run the workplan/blueprint gate first." The full multi-surface sequence is under "Blueprint sign-off" below; if you are unsure which path you are on, run `vise plan` and check for a `socialWorkplan` (multi-surface) or an `experienceHint` (a single-outcome request that likely wants expansion — run `vise creative`).
|
|
23
|
+
|
|
22
24
|
For a **feed / community / experience** request, also run `vise creative . --request "<user intent>"` (the advisory Engagement Intelligence layer) before building — it surfaces engagement options (post types, polls, reactions, discovery/achievement loops) that `vise plan` only lists tersely under `optionalCapabilities` / `engagement`. Advisory, never a gate — full guidance under "Creative pre-planning" below.
|
|
23
25
|
|
|
24
|
-
**You own the goal→experience inference; EI is advisory data, not a decider.**
|
|
26
|
+
**You own the goal→experience inference; EI is advisory data, not a decider.** Multi-surface `socialWorkplan` expansion is driven by an accepted EI selection, not raw request keyword matching. If the goal is experience-shaped, run `vise creative . --request "<goal>"`, read its `candidateSolutions` as the advisory **menu** of experiences, and **infer which one fits the user's intent yourself** — mapping a fuzzy goal to an experience is your job (you are the inferential layer), not a keyword match (`creative`'s ranking is advisory; the *right* candidate is often in the set but not ranked first). Then accept the chosen variant so `vise workplan` and the blueprint gate have a concrete companion-surface sequence to govern. Never treat `vise plan` returning no `socialWorkplan` before `creative accept` as proof that no fuller experience applies.
|
|
25
27
|
|
|
26
28
|
Before accepting a variant, **show the EI choice explicitly**. Do not compress this step into one sentence like "community-first is best." Present a compact decision block with:
|
|
27
29
|
- recommended experience and 2-3 concrete request/project signals that make it fit;
|
|
@@ -77,7 +79,7 @@ renders feed, composer, comments, reactions, profile/follow, moderation, or chat
|
|
|
77
79
|
- If data is absent, ask whether to prepare it in Console or create safe seed data through SDK functions before judging the integration. If they decline, say that only static checks / resolved empty states can be claimed, not populated product proof.
|
|
78
80
|
- When safe SDK seed data is created, keep the returned community/feed/post/comment objects in runtime state or wait for a confirmed re-query/live-collection update before final handoff. A default path that still shows `0 posts`, `syncing`, `waiting on content`, create/prepare failure copy, or only disabled affordances is not populated runtime proof. If the exact product-context target cannot be created or populated because of tenant permissions or review mode, fall back to the nearest discovered readable target with visible posts/messages/users and map it back into the product context with honest copy. Do not silently swallow seed/query errors; record failures in handoff notes and show a product-native retry/error state.
|
|
79
81
|
- Do not dump arbitrary readable tenant data into product UI just because discovery returned it. Filter discovered communities/users/channels by product context, or create a product-native seed target. Names/descriptions that are raw IDs, numeric-only labels, short random strings such as `rand2`, lorem/test data, `sampleapp` fixtures, or unrelated generated business copy are not natural targets; hide or skip them and keep searching/seeding before claiming runtime proof. Prefer a product-owned namespace/allowlist such as app-created names, metadata tags, session/program identifiers, or cached IDs returned by the app's own create flow; if the SDK query returns mixed tenant data, render only product-safe matches and never let an unsafe target become the active selected context. Deduplicate discovered/seeded targets by stable SDK ID or normalized product label, and prioritize populated product-owned targets before empty clones; a long rail of repeated zero-post targets is not polished populated proof. Do not rewrite unsafe tenant labels into product copy to make them look acceptable — exclude the object. Apply this filtering to every visible rail/list/card and author fallback, not only the selected target.
|
|
80
|
-
- Runtime proof can consume browser/simulator/emulator/device resources and additional agent tokens. Ask the user before running it; if they decline, explain the remaining runtime risks instead of claiming the app was proven at runtime.
|
|
82
|
+
- Runtime proof can consume browser/simulator/emulator/device resources and additional agent tokens. Ask the user before running it; if they decline, explain the remaining runtime risks instead of claiming the app was proven at runtime. **Precedence (resolves the apparent conflict with "Pre-authorized dogfood/benchmark sign-off" above):** the "ask first" rule is the default for an interactive session. A dogfood/benchmark prompt that *explicitly* grants runtime-proof consent (per that clause) IS the user's answer — do not re-ask in that run. If consent was neither given interactively nor explicitly in the prompt, do not run device/browser proof: proceed with static checks and say so. Never infer consent from silence.
|
|
81
83
|
- Record the expected smoke level: use `populated` for data-backed surfaces where empty screens are not product proof, and `resolved` only where an empty state is legitimate.
|
|
82
84
|
- Never delete a runnable entry file such as `App.tsx`, `lib/main.dart`, or the app's root view as a standalone step while preparing a replacement. Edit it in place, or add/import a new child component/widget first, so interruptions and failed patches do not leave the project without an entry point.
|
|
83
85
|
- Record runtime readiness/proof status in evidence files, screenshots, or handoff notes — not as end-user product UI. Do not ship visible `CLIENT`, `SESSION`, `RUNTIME`, SDK, env-var, region, setup, readiness-debug labels, runtime/probe/status panels, or hidden no-op controls just to prove the app connected; the normal screen should express the product state naturally.
|
|
@@ -85,7 +87,7 @@ renders feed, composer, comments, reactions, profile/follow, moderation, or chat
|
|
|
85
87
|
- A repository query that stores SDK objects in state but never renders or passes that state into a visible widget/component is a hidden no-op, not runtime proof. The first social surface must consume SDK-returned communities/posts/comments/users in the actual product tree; returning the original screen after setup/login is still a failed integration.
|
|
86
88
|
- Native runtime proof must make the core social loop visibly reachable. Do not spend the whole first viewport on intro, hero, or community chrome while feed/composer/comment/reaction/report/profile surfaces are buried below an unverified scroll. For Flutter and React Native dogfood, keep at least one SDK-backed feed item and key actions visible in the initial route, or expose them through obvious product tabs/buttons that screenshots can capture; automated text/tap probes may not see Flutter/RN accessibility text.
|
|
87
89
|
- Runtime screenshots must be product-clean. Flutter/React Native debug artifacts such as yellow/black `RenderFlex` overflow banners, `BOTTOM OVERFLOWED BY ... PIXELS`, red error boxes, or clipped action rows are runtime failures to repair, not acceptable evidence. Use bounded card dimensions, `Flexible`/`Expanded`/`Wrap`, `maxLines`, ellipsis, and responsive spacing before handoff.
|
|
88
|
-
- If `vise check` fails `<platform>.live-collection.session-ready-requery` because `sp-vise/evidence/runtime-smoke.json` is missing or failed, do **not** attest it and do not call the surface complete. Declare `sp-vise/smoke.json` for the mounted collection surfaces, emit log-only markers from the real SDK query lifecycle (`VISE_SMOKE surface=<id> state=loading|empty|loaded count=N|error:<message>`), cold-launch the app into the social route, capture the device/browser log, run `vise smoke . --log <capture>`, and rerun `vise check . --ci`. The markers must never include secrets, PII, raw social IDs, or end-user setup/debug labels; they are evidence artifacts, not product UI. For Flutter/React Native dogfood, capture bounded Android logcat/simulator logs before `workplan complete` when the surface uses live collections.
|
|
90
|
+
**Recovering a red `<platform>.live-collection.session-ready-requery` (this is a GATE remedy — NOT advisory like the bullets above; the surface cannot go green or be completed without it).** If `vise check` fails `<platform>.live-collection.session-ready-requery` because `sp-vise/evidence/runtime-smoke.json` is missing or failed, do **not** attest it and do not call the surface complete. Declare `sp-vise/smoke.json` for the mounted collection surfaces, emit log-only markers from the real SDK query lifecycle (`VISE_SMOKE surface=<id> state=loading|empty|loaded count=N|error:<message>`), cold-launch the app into the social route, capture the device/browser log, run `vise smoke . --log <capture>`, and rerun `vise check . --ci`. The markers must never include secrets, PII, raw social IDs, or end-user setup/debug labels; they are evidence artifacts, not product UI. For Flutter/React Native dogfood, capture bounded Android logcat/simulator logs before `workplan complete` when the surface uses live collections. (See also `vise debug` — the requery rules now carry session-state symptoms, so a pasted `notLoggedIn`/`session not established` crash correlates here.)
|
|
89
91
|
|
|
90
92
|
Implement one surface at a time. Surface that focused plan's intake questions to
|
|
91
93
|
the user, including opt-in capability questions, then run the normal check loop.
|
|
@@ -104,9 +106,10 @@ vise workplan status . --request "<user intent>"
|
|
|
104
106
|
|
|
105
107
|
`workplan complete` is evidence-backed: it refuses non-green `vise check`
|
|
106
108
|
results and snapshots the surface's compliance/check sidecar under
|
|
107
|
-
`sp-vise/workplan-snapshots/<surface
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
`sp-vise/workplan-snapshots/<surface>/` with a snapshot integrity manifest.
|
|
110
|
+
`workplan status` ignores missing or drifted snapshot evidence. Repeat until it
|
|
111
|
+
reports the broad request complete, then run one final `vise check`,
|
|
112
|
+
`vise validate`, and `vise run-sensors` before handoff.
|
|
110
113
|
|
|
111
114
|
Then fetch docs cited by the plan or relevant to the requested feature:
|
|
112
115
|
|
|
@@ -115,7 +118,7 @@ vise search-docs "<SDK feature or API pattern>"
|
|
|
115
118
|
vise get-doc-page "<canonical-doc-path>"
|
|
116
119
|
```
|
|
117
120
|
|
|
118
|
-
**Implementation pacing matters.** Once the blueprint/intake gates are clear, make a visible product-source edit before broad research: route the host entry point to the new social surface, or create and import a product-native component for that surface. In bounded dogfood/benchmark runs, this is a hard ordering requirement: after the first `vise init` or focused surface command clears, edit product source before fetching a second docs page, grepping generated SDK/framework interfaces, resolving broad native package internals, or polishing UI. Do not create a shell-first component; the first social component should already import the relevant social.plus SDK package and contain setup/login plus at least one SDK data API call. If you need exact symbols, use package-root imports first, inspect the narrow package-root exports or one targeted type file, then let TypeScript/build errors guide the next lookup. Do not stop at static feature-preview cards that merely describe feed/comments/reactions/profile/chat; those are product copy, not integration. A run that only creates `sp-vise/` artifacts, installs dependencies, reads docs, edits dependency manifests/project files, or stops at a product-looking shell without SDK-backed source is not a completed Vise implementation.
|
|
121
|
+
**Implementation pacing matters.** Once the blueprint/intake gates are clear, make a visible product-source edit before broad research: route the host entry point to the new social surface, or create and import a product-native component for that surface. In bounded dogfood/benchmark runs, this is a hard ordering requirement: after the first `vise init` or focused surface command clears, edit product source before fetching a second docs page, grepping generated SDK/framework interfaces, resolving broad native package internals, or polishing UI. (No contradiction with the Required Loop / per-feature `vise search-docs` recipes: the *first* docs fetch that the plan or feature recipe calls for is expected — this rule only bars *stacking more* research before your first SDK-backed edit. Fetch once, edit, then let build/type errors drive further lookups.) Do not create a shell-first component; the first social component should already import the relevant social.plus SDK package and contain setup/login plus at least one SDK data API call. If you need exact symbols, use package-root imports first, inspect the narrow package-root exports or one targeted type file, then let TypeScript/build errors guide the next lookup. Do not stop at static feature-preview cards that merely describe feed/comments/reactions/profile/chat; those are product copy, not integration. A run that only creates `sp-vise/` artifacts, installs dependencies, reads docs, edits dependency manifests/project files, or stops at a product-looking shell without SDK-backed source is not a completed Vise implementation.
|
|
119
122
|
|
|
120
123
|
The visible edit must also **use** the SDK data it fetches. If you add `_communities`, `posts`, `comments`, or similar SDK-backed state, render it or pass it into a rendered child before moving on. Unused SDK state, hidden controls, `void Repository` markers, iOS `let _ = AmityCommunityRepository.self` / type-only repository references, or a wrapper that still returns the original product screen are scanner-only integration and should be fixed before `vise check`.
|
|
121
124
|
|
|
@@ -166,6 +169,17 @@ Treat Vise runtime smoke sensors as real validation. For TypeScript/React Native
|
|
|
166
169
|
|
|
167
170
|
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.
|
|
168
171
|
|
|
172
|
+
## Resuming an in-progress flow
|
|
173
|
+
|
|
174
|
+
The `sp-vise/` sidecar IS the flow's state — Vise re-derives from it on every command, so a flow is resumable across turns and never restarts from scratch. When the user says "continue" (or you pick up a partially-built integration):
|
|
175
|
+
|
|
176
|
+
- **Run `vise workplan status . --request "<intent>"` FIRST.** It reads the sidecar directly and tells you exactly where the journey stands — completed surfaces, the next pending surface, the accepted experience, and the signed blueprint — so you don't re-plan or re-ask decisions Vise already recorded (an accepted `vise creative` variant, the signed `blueprint_confirmation`, the chosen `solution_path`, and completed surfaces all persist and are replayed).
|
|
177
|
+
- **Prefer the ORIGINAL request wording.** Resume matching is deterministic (not semantic): the same/substring request, or one whose resolved outcome is a stage of the signed journey (or a companion — comments↔feed), re-attaches. A loosely reworded request may not; when in doubt, reuse the exact original request string.
|
|
178
|
+
- **`status: "not-applicable"` does not mean "nothing to do."** If a signed multi-surface flow exists but your request didn't re-attach, the response carries a `signedFlowAvailable` block naming the canonical request and how to resume. Re-run with that request rather than starting a fresh single-surface build that would orphan the journey.
|
|
179
|
+
- **Do not hand-edit the sidecar to "fix" state.** `flow.json`, `workplan.json`, and the snapshot manifests are integrity-checked; a hand-authored progress/snapshot entry is ignored. Drive state only through the `vise` commands.
|
|
180
|
+
|
|
181
|
+
**When runtime proof can't be produced (declined / deviceless CI).** If the user declines device/emulator runtime proof, or you're in a deviceless environment, do not leave a live-collection surface stuck red-forever and do not fake evidence. Record an honest, auditable waiver: `vise smoke waive . --reason "<why proof isn't being run>" --mode declined|deviceless`. `vise check` then reports `runtime-proof-waived` (exit 9 — CI still blocks by default; pass `--allow-proof-waiver` to accept it and exit 0), and `vise workplan complete` will record the surface with the waiver noted. A waiver is an explicit UNDER-claim — never report a waived surface as "runtime-proven"; state in the handoff that only static checks are claimed. Remove `sp-vise/evidence/runtime-proof-waiver.json` once real proof is captured; genuine passing runtime-smoke evidence always overrides a waiver.
|
|
182
|
+
|
|
169
183
|
## Prefer Installable Blocks When a Registry Is Available (preview)
|
|
170
184
|
|
|
171
185
|
**Installable social.plus blocks are a preview capability that requires a block registry.** A registry is not generally available yet, so unless the user explicitly provides a `--registry <path>` or `sp-vise/blocks.json` already exists in the project, hand-build the surface as normal — the rest of this skill fully governs the hand-built path. When a registry IS provided, run `vise blocks list --registry <path>` before implementing the surface by hand. When a block exists for the outcome, prefer the governed install path:
|
|
@@ -182,7 +196,7 @@ No registry available means hand-build as normal — never assume a block or a r
|
|
|
182
196
|
|
|
183
197
|
## Debugging (Day 2 Operations)
|
|
184
198
|
|
|
185
|
-
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
|
|
199
|
+
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 whose stack/blame points at a file that imports the SDK, or that surfaced on an SDK-backed social surface** (feed/comments/chat/community/profile). The error *text* need not contain those keywords — a generic-looking `NullPointerException`, `401`, `OutOfMemoryError`, or a `notLoggedIn`/`session not established` message thrown from the social path IS in scope (that is exactly the flagship "feed stays empty / signing-in" class). Skip only genuinely non-SDK UI/CSS/build issues unrelated to any social surface. Full `vise debug` workflow and `repairBrief` format: **see `reference/debugging.md`**.
|
|
186
200
|
|
|
187
201
|
(If `vise` isn't installed: `npm install -g @amityco/social-plus-vise`. Engagement-scope handling — `sp-vise/engagement.json` — is in `reference/operations.md`.)
|
|
188
202
|
|
|
@@ -290,7 +304,7 @@ A read *count* with no way to clear it is half a feature: when a channel/thread
|
|
|
290
304
|
|
|
291
305
|
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.
|
|
292
306
|
|
|
293
|
-
- **Correct pattern**: Observe each message's `syncState` and surface failed sends with a retry/delete affordance;
|
|
307
|
+
- **Correct pattern**: Observe each message's `syncState` and surface failed sends with a retry/delete affordance; where the platform SDK exposes a failed-message cleanup helper, use it on init, otherwise keep retry/delete behavior in product state with supported message APIs.
|
|
294
308
|
- **Wrong pattern**: Calling `createMessage` and rendering success unconditionally, with no handling for the `error` syncState.
|
|
295
309
|
|
|
296
310
|
### File Upload & Media
|
|
@@ -316,6 +330,12 @@ When querying lists of entities (e.g., `getPosts`, `getCommunityFeed`), always u
|
|
|
316
330
|
|
|
317
331
|
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.
|
|
318
332
|
|
|
333
|
+
### Seeding Data When Empty (read-after-write lag)
|
|
334
|
+
When a surface has no product data and you create safe seed data through the SDK (`createCommunity` / `createChannel` / `createPost`), a **freshly created object does not reliably reappear in an immediate list re-query** — the backing list index lags the write, often returning empty across several cold-launch attempts. Treating the empty re-query as "no data" is the single biggest time-sink when populating a first surface (observed repeatedly on native dogfoods where a community was created but the crew list re-query stayed empty).
|
|
335
|
+
- **Correct pattern:** Merge the create-response object **directly into the rendered/published list** (keyed by its returned ID) instead of waiting for the list re-query to catch up. **Persist created IDs** to local storage/cache (`UserDefaults`/`localStorage`/prefs — never a source constant) so a relaunch rehydrates via a **direct `getCommunity(id)` / `getChannel(id)` fetch** (immune to list-index lag) rather than re-seeding duplicates.
|
|
336
|
+
- **Wrong pattern:** `create(...)` → immediately re-query the list → render the (still-empty) list → conclude "seeding failed / no data". Also wrong: re-seeding on every launch because the previous run's IDs weren't persisted, producing a rail of duplicate crews.
|
|
337
|
+
- **Bounded retry, not give-up or infinite-spin:** a cold launch can transiently return `401`/`notLoggedIn` or a stalled first query before the session settles (see Session Renewal). Wrap seeding + the first query in a **bounded** retry (a few attempts with backoff) that ends in a product-native retry affordance — never an unbounded loop and never a silent swallow. Do not fire the seed/query in the same synchronous tick as `login()`.
|
|
338
|
+
|
|
319
339
|
### Post Status Filters
|
|
320
340
|
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.
|
|
321
341
|
- **Correct pattern:** Apply `.includeDeleted(false)`, `.statuses([.published])`, or `.feedType(AmityFeedType.PUBLISHED)` to your query builders.
|
|
@@ -354,8 +374,8 @@ A comment thread is incomplete if it only lists comments. Users expect to post.
|
|
|
354
374
|
|
|
355
375
|
### Explicit Feed Target Types
|
|
356
376
|
When calling `createPost`, do not hardcode the `targetType` to a literal value like `AmityPostTargetType.COMMUNITY` if the composer component is meant to be reusable.
|
|
357
|
-
- **Correct pattern:** Pass the `targetType` dynamically (e.g. from props, parameters, or intent extras) so the composer can post to a user
|
|
358
|
-
- **Wrong pattern:** Hardcoding `targetType: AmityPostTargetType.COMMUNITY` inside a generic post composer.
|
|
377
|
+
- **Correct pattern:** Pass the `targetType` dynamically (e.g. from props, parameters, or intent extras) so the composer can post to a valid SDK target: user or community. A global feed is an aggregate read surface from followed users and joined communities; it is not a post creation target.
|
|
378
|
+
- **Wrong pattern:** Hardcoding `targetType: AmityPostTargetType.COMMUNITY` inside a generic post composer, or offering `global` as a createPost target.
|
|
359
379
|
|
|
360
380
|
### Comment Reference Type Enum
|
|
361
381
|
When creating comments, always use the SDK's provided enum types for the reference type rather than raw string literals.
|
|
@@ -406,7 +426,7 @@ On **iOS**, the SwiftPM package is `github.com/AmityCo/Amity-Social-Cloud-SDK-iO
|
|
|
406
426
|
|
|
407
427
|
## Resolving `vise check` Findings
|
|
408
428
|
|
|
409
|
-
`vise check` reports
|
|
429
|
+
`vise check` reports overall statuses such as `green`, `needs-attestation`, `deterministic-failures`, `blocked`, `contract-drift`, `completeness-gap`, `selected-capability-failures`, `no-platform` (no social.plus SDK platform detected — reported as a failure, never a vacuous green), and `runtime-proof-waived` (see the runtime-proof waiver note above); individual rules use `deterministic-pass`, `deterministic-fail`, `attestation-needed`, `blocked`, `stale`, or `advisory`. **Non-green statuses 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 and the rule allows attestation. Confidence honesty, the `vise attest` evidence-file format, escalation/human-signer rules, and "`vise sync` is not attestation": **see `reference/operations.md`**.
|
|
410
430
|
|
|
411
431
|
## Feature Guidance
|
|
412
432
|
|
|
@@ -520,7 +540,7 @@ vise search-docs "community create join leave members invitation"
|
|
|
520
540
|
vise plan . --request "<community request>"
|
|
521
541
|
```
|
|
522
542
|
|
|
523
|
-
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 natural community source (discovery query, create-flow return value, route/app state, or user selection; 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>`.
|
|
543
|
+
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 natural community source (discovery query, create-flow return value, route/app state, or user selection; 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>`.
|
|
524
544
|
|
|
525
545
|
For the social graph (follow/followers):
|
|
526
546
|
|