@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.
@@ -0,0 +1,129 @@
1
+ // SDK-version currency guidance for `vise plan` (advisory, never gates).
2
+ //
3
+ // Why this exists: an agent (or a copy-pasted example) can pin an arbitrary, stale
4
+ // SDK version — e.g. @amityco/ts-sdk@6.0.0 when 7.x is current — which silently
5
+ // loses newer APIs (a real run opted out of `story` because StoryRepository didn't
6
+ // exist in 6.0.0). The social.plus docs Vise fetches don't expose a parseable
7
+ // version (changelogs are feature-organized), but the npm registry does, exactly
8
+ // and live. So for the npm-published SDKs (TypeScript / React Native) we look up the
9
+ // registry `latest`; for Android/iOS/Flutter (Maven/CocoaPods/pub — multi-registry)
10
+ // we give version-agnostic "install latest, then pin" guidance.
11
+ //
12
+ // This runs in the PLAN layer (which already does network I/O for docs), NOT in the
13
+ // deterministic offline validators. It degrades gracefully: any fetch failure falls
14
+ // back to version-agnostic guidance.
15
+ import { fetchTextWithTimeout } from "./docs.js";
16
+ // npm package per platform. React Native ships a distinct package with its own
17
+ // version line. Android/iOS/Flutter are not npm-published → undefined.
18
+ function npmPackageFor(platform) {
19
+ if (platform === "typescript")
20
+ return "@amityco/ts-sdk";
21
+ if (platform === "react-native")
22
+ return "@amityco/ts-sdk-react-native";
23
+ return undefined;
24
+ }
25
+ const VERSION_AGNOSTIC = "Greenfield install: add the social.plus SDK at its latest version, then pin the resolved version in your manifest for reproducible CI. Do not copy a version number from examples, docs snippets, or memory — it is likely stale.";
26
+ function majorOf(version) {
27
+ if (!version)
28
+ return null;
29
+ const m = version.match(/(\d+)\.\d+(?:\.\d+)?/); // tolerates ^, ~, >=, ranges
30
+ return m ? Number(m[1]) : null;
31
+ }
32
+ // Pure logic: given the platform, the project's currently-pinned version (or null for
33
+ // greenfield), and the registry latest (or null when unavailable), produce guidance.
34
+ // Separated from the fetch so it is deterministically testable.
35
+ export function composeSdkVersionGuidance(platform, currentPinned, latest) {
36
+ const pkg = npmPackageFor(platform);
37
+ // Non-npm platforms, or npm lookup unavailable (offline) → version-agnostic guidance,
38
+ // but only worth surfacing for a greenfield project (no existing pin to respect).
39
+ if (!latest) {
40
+ return currentPinned ? undefined : { kind: "info", advice: VERSION_AGNOSTIC };
41
+ }
42
+ if (!currentPinned) {
43
+ return {
44
+ kind: "info",
45
+ latest,
46
+ advice: `Greenfield install: use ${pkg}@${latest} (current latest on npm) and pin it in package.json. Do not copy an older version from examples or memory.`,
47
+ };
48
+ }
49
+ const curMajor = majorOf(currentPinned);
50
+ const latMajor = majorOf(latest);
51
+ if (curMajor !== null && latMajor !== null && curMajor < latMajor) {
52
+ return {
53
+ kind: "fyi",
54
+ latest,
55
+ current: currentPinned,
56
+ advice: `This project pins ${pkg} ${currentPinned}, but the current latest is ${latest} (major ${latMajor}). A major upgrade is a separate, out-of-scope decision for this task — flagging for awareness only; do not upgrade as part of this change.`,
57
+ };
58
+ }
59
+ // On the current major (or ahead) — nothing to surface.
60
+ return undefined;
61
+ }
62
+ // Read the project's pinned version of `pkgName` from package.json (null if absent or
63
+ // a non-pinned placeholder). Best-effort; never throws.
64
+ export function pinnedVersion(packageJsonText, pkgName) {
65
+ if (!packageJsonText)
66
+ return null;
67
+ try {
68
+ const pkg = JSON.parse(packageJsonText);
69
+ const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
70
+ const v = deps[pkgName];
71
+ if (typeof v !== "string")
72
+ return null;
73
+ if (/^(?:latest|\*|x|X)$/i.test(v.trim()))
74
+ return null; // floating — the pinned rule handles this
75
+ return v.trim();
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
81
+ const cache = new Map();
82
+ const REGISTRY_TTL_MS = 60 * 60 * 1000; // 1h, mirrors the docs cache TTL
83
+ // Tight, dedicated timeout — NOT the 15s docs default. Every CLI `vise plan` is a
84
+ // fresh process with a cold cache, so an unreachable registry would otherwise add the
85
+ // full docs timeout to a core command for a merely-advisory field. Bounded at ~2.5s.
86
+ const REGISTRY_TIMEOUT_MS = 2500;
87
+ function nowMs() {
88
+ // Date.now is unavailable in some sandboxed contexts; guard so a lookup failure
89
+ // never throws (it just skips caching).
90
+ try {
91
+ return Date.now();
92
+ }
93
+ catch {
94
+ return 0;
95
+ }
96
+ }
97
+ // Fetch the registry `latest` dist-tag for a scoped npm package. Returns null on any
98
+ // failure (offline, timeout, parse error) so the plan degrades gracefully.
99
+ export async function fetchNpmLatest(pkg) {
100
+ const now = nowMs();
101
+ const cached = cache.get(pkg);
102
+ if (cached && now > 0 && now - cached.at < REGISTRY_TTL_MS)
103
+ return cached.version;
104
+ let version = null;
105
+ try {
106
+ const base = (process.env.NPM_REGISTRY_BASE_URL ?? "https://registry.npmjs.org").replace(/\/+$/, "");
107
+ const url = `${base}/-/package/${pkg.replace("/", "%2F")}/dist-tags`;
108
+ const body = await fetchTextWithTimeout(url, "application/json", REGISTRY_TIMEOUT_MS);
109
+ const parsed = JSON.parse(body);
110
+ version = typeof parsed?.latest === "string" ? parsed.latest : null;
111
+ }
112
+ catch {
113
+ version = null;
114
+ }
115
+ if (now > 0)
116
+ cache.set(pkg, { version, at: now });
117
+ return version;
118
+ }
119
+ // Plan-layer entry point: produce advisory SDK-version guidance for a project.
120
+ export async function sdkVersionGuidance(platform, packageJsonText) {
121
+ const pkg = npmPackageFor(platform);
122
+ if (!pkg) {
123
+ // Android/iOS/Flutter — version-agnostic, greenfield-oriented guidance only.
124
+ return { kind: "info", advice: VERSION_AGNOSTIC };
125
+ }
126
+ const latest = await fetchNpmLatest(pkg);
127
+ const currentPinned = pinnedVersion(packageJsonText, pkg);
128
+ return composeSdkVersionGuidance(platform, currentPinned, latest);
129
+ }
package/dist/types.js CHANGED
@@ -29,3 +29,7 @@ export function optionalNumberField(input, field, fallback) {
29
29
  const value = input[field];
30
30
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
31
31
  }
32
+ export function optionalBooleanField(input, field, fallback = false) {
33
+ const value = input[field];
34
+ return typeof value === "boolean" ? value : fallback;
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amityco/social-plus-vise",
3
- "version": "0.8.1",
3
+ "version": "0.12.2",
4
4
  "description": "Skill-guided deterministic CLI for social.plus SDK integration assistance.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -28,20 +28,25 @@
28
28
  "bin": {
29
29
  "vise": "dist/server.js",
30
30
  "spf": "dist/server.js",
31
- "social-plus-vise": "dist/server.js"
31
+ "social-plus-vise": "dist/server.js",
32
+ "foundry": "dist/server.js",
33
+ "social-plus-foundry": "dist/server.js",
34
+ "foundry-mcp": "dist/server.js"
32
35
  },
33
36
  "files": [
34
37
  "dist",
35
38
  "LICENSE",
36
39
  "README.md",
40
+ "CHANGELOG.md",
41
+ "social.plus-vise.png",
37
42
  "rules",
38
43
  "skills"
39
44
  ],
40
45
  "scripts": {
41
46
  "build": "tsc -p tsconfig.json",
42
47
  "postbuild": "chmod +x dist/server.js",
43
- "pack:check": "npm pack --dry-run --cache /private/tmp/social-plus-foundry-npm-cache",
44
- "publish:check": "npm run validate && npm publish --dry-run --access public --cache /private/tmp/social-plus-foundry-npm-cache",
48
+ "pack:check": "npm pack --dry-run --cache /tmp/social-plus-foundry-npm-cache",
49
+ "publish:check": "npm run validate && npm publish --dry-run --access public --cache /tmp/social-plus-foundry-npm-cache",
45
50
  "start": "node dist/server.js",
46
51
  "test": "npm run build && node test/run-fixtures.mjs",
47
52
  "test:cli": "npm run build && node test/run-cli.mjs",
@@ -53,9 +58,25 @@
53
58
  "test:rule-coverage": "npm run build && node test/run-rule-coverage.mjs",
54
59
  "test:happy-path-clean": "npm run build && node test/run-happy-path-clean.mjs",
55
60
  "test:fixture-symmetry": "npm run build && node test/run-fixture-symmetry.mjs",
61
+ "test:nonui-skip": "npm run build && node test/run-nonui-layer-skip.mjs",
62
+ "test:sdk-version": "npm run build && node test/run-sdk-version.mjs",
56
63
  "typecheck": "tsc -p tsconfig.json --noEmit",
57
- "validate": "npm run typecheck && npm test && npm run test:mcp && npm run test:cli && npm run test:docs && npm run test:ast && npm run test:compliance && npm run test:rule-coverage && npm run test:readme-coverage && npm run test:happy-path-clean && npm run test:fixture-symmetry && npm run test:improvements && npm run pack:check",
58
- "test:ast": "node test/run-ast-helpers.mjs"
64
+ "test:e2e-package": "npm run build && node test/run-e2e-package.mjs",
65
+ "validate": "npm run typecheck && npm test && npm run test:mcp && npm run test:cli && npm run test:docs && npm run test:ast && npm run test:design-extract && npm run test:capabilities && npm run test:classify && npm run test:compliance && npm run test:rule-coverage && npm run test:readme-coverage && npm run test:happy-path-clean && npm run test:fixture-symmetry && npm run test:nonui-skip && npm run test:sdk-version && npm run test:native-idioms && npm run test:grader-facts && npm run test:ground-truth && npm run test:improvements && npm run test:debug && npm run test:preflight && npm run test:e2e-package && npm run pack:check",
66
+ "test:ast": "node test/run-ast-helpers.mjs",
67
+ "test:design-extract": "npm run build && node test/run-design-extract.mjs",
68
+ "test:capabilities": "npm run build && node test/run-capabilities.mjs",
69
+ "test:classify": "npm run build && node test/run-classify.mjs",
70
+ "test:debug": "npm run build && node test/run-debug.mjs",
71
+ "test:preflight": "npm run build && node test/run-preflight.mjs",
72
+ "test:native-idioms": "npm run build && node test/run-native-idioms.mjs",
73
+ "test:grader-facts": "node test/run-grader-facts.mjs",
74
+ "test:ground-truth": "node test/run-ground-truth.mjs",
75
+ "bench:tp": "npm run build && node bench/tp-dashboard.mjs",
76
+ "bench:ground-truth": "node bench/ground-truth.mjs",
77
+ "bench:symbols-drift": "node bench/grader/build-symbols.mjs --check",
78
+ "bench:audit-boundaries": "node bench/audit-marker-boundaries.mjs",
79
+ "bench:gate": "npm run build && node bench/tp-dashboard.mjs && node test/run-fixture-symmetry.mjs && node test/run-native-idioms.mjs && node test/run-grader-facts.mjs && node test/run-happy-path-clean.mjs"
59
80
  },
60
81
  "dependencies": {
61
82
  "@modelcontextprotocol/sdk": "^1.12.0",
package/rules/auth.yaml CHANGED
@@ -7,36 +7,123 @@
7
7
  "version": 1,
8
8
  "title": "Android must logout before switching user identity",
9
9
  "severity": "warning",
10
- "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users.",
11
- "applies_when": { "platforms": ["android"], "outcomes": ["validate-setup"] },
10
+ "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users. Insert logout-before-login in the existing switch flow without dropping any existing renewal or session-handler behavior on the login call.",
11
+ "applies_when": {
12
+ "platforms": [
13
+ "android"
14
+ ],
15
+ "outcomes": [
16
+ "validate-setup"
17
+ ]
18
+ },
12
19
  "enforcement": {
13
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "android.auth.logout-on-user-switch" }],
14
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "user_switch_flow", "description": "Code path showing logout/disconnect before login as a different user.", "upload_policy": "upload-with-consent" }] }
15
- }
20
+ "deterministic": [
21
+ {
22
+ "check": "validator-finding-absent",
23
+ "finding_rule_id": "android.auth.logout-on-user-switch"
24
+ }
25
+ ],
26
+ "attestation": {
27
+ "allowed": true,
28
+ "host_agent_min_confidence": "high",
29
+ "human_allowed": true,
30
+ "evidence_required": [
31
+ {
32
+ "field": "user_switch_flow",
33
+ "description": "Code path showing logout/disconnect before login as a different user.",
34
+ "upload_policy": "upload-with-consent"
35
+ }
36
+ ]
37
+ }
38
+ },
39
+ "symptoms": [
40
+ "NullPointerException",
41
+ "Session conflict",
42
+ "session already exists",
43
+ "cross-user"
44
+ ]
16
45
  },
17
46
  {
18
47
  "id": "flutter.auth.logout-on-user-switch",
19
48
  "version": 1,
20
49
  "title": "Flutter must logout before switching user identity",
21
50
  "severity": "warning",
22
- "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users.",
23
- "applies_when": { "platforms": ["flutter"], "outcomes": ["validate-setup"] },
51
+ "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users. Insert logout-before-login in the existing switch flow without dropping any existing renewal or sessionHandler behavior on the subsequent login call.",
52
+ "applies_when": {
53
+ "platforms": [
54
+ "flutter"
55
+ ],
56
+ "outcomes": [
57
+ "validate-setup"
58
+ ]
59
+ },
24
60
  "enforcement": {
25
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "flutter.auth.logout-on-user-switch" }],
26
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "user_switch_flow", "description": "Code path showing logout/disconnect before login as a different user.", "upload_policy": "upload-with-consent" }] }
27
- }
61
+ "deterministic": [
62
+ {
63
+ "check": "validator-finding-absent",
64
+ "finding_rule_id": "flutter.auth.logout-on-user-switch"
65
+ }
66
+ ],
67
+ "attestation": {
68
+ "allowed": true,
69
+ "host_agent_min_confidence": "high",
70
+ "human_allowed": true,
71
+ "evidence_required": [
72
+ {
73
+ "field": "user_switch_flow",
74
+ "description": "Code path showing logout/disconnect before login as a different user.",
75
+ "upload_policy": "upload-with-consent"
76
+ }
77
+ ]
78
+ }
79
+ },
80
+ "symptoms": [
81
+ "NullPointerException",
82
+ "Session conflict",
83
+ "session already exists",
84
+ "cross-user"
85
+ ]
28
86
  },
29
87
  {
30
88
  "id": "typescript.auth.logout-on-user-switch",
31
89
  "version": 1,
32
90
  "title": "TypeScript must logout before switching user identity",
33
91
  "severity": "warning",
34
- "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users.",
35
- "applies_when": { "platforms": ["typescript"], "outcomes": ["validate-setup"] },
92
+ "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users. Insert logout-before-login in the existing switch flow without dropping any existing renewal or sessionHandler behavior on the subsequent login call.",
93
+ "applies_when": {
94
+ "platforms": [
95
+ "typescript"
96
+ ],
97
+ "outcomes": [
98
+ "validate-setup"
99
+ ]
100
+ },
36
101
  "enforcement": {
37
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "typescript.auth.logout-on-user-switch" }],
38
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "user_switch_flow", "description": "Code path showing logout/disconnect before login as a different user.", "upload_policy": "upload-with-consent" }] }
39
- }
102
+ "deterministic": [
103
+ {
104
+ "check": "validator-finding-absent",
105
+ "finding_rule_id": "typescript.auth.logout-on-user-switch"
106
+ }
107
+ ],
108
+ "attestation": {
109
+ "allowed": true,
110
+ "host_agent_min_confidence": "high",
111
+ "human_allowed": true,
112
+ "evidence_required": [
113
+ {
114
+ "field": "user_switch_flow",
115
+ "description": "Code path showing logout/disconnect before login as a different user.",
116
+ "upload_policy": "upload-with-consent"
117
+ }
118
+ ]
119
+ }
120
+ },
121
+ "symptoms": [
122
+ "NullPointerException",
123
+ "Session conflict",
124
+ "session already exists",
125
+ "cross-user"
126
+ ]
40
127
  },
41
128
  {
42
129
  "id": "react-native.auth.logout-on-user-switch",
@@ -44,11 +131,40 @@
44
131
  "title": "React Native must logout before switching user identity",
45
132
  "severity": "warning",
46
133
  "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users.",
47
- "applies_when": { "platforms": ["react-native"], "outcomes": ["validate-setup"] },
134
+ "applies_when": {
135
+ "platforms": [
136
+ "react-native"
137
+ ],
138
+ "outcomes": [
139
+ "validate-setup"
140
+ ]
141
+ },
48
142
  "enforcement": {
49
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "react-native.auth.logout-on-user-switch" }],
50
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "user_switch_flow", "description": "Code path showing logout/disconnect before login as a different user.", "upload_policy": "upload-with-consent" }] }
51
- }
143
+ "deterministic": [
144
+ {
145
+ "check": "validator-finding-absent",
146
+ "finding_rule_id": "react-native.auth.logout-on-user-switch"
147
+ }
148
+ ],
149
+ "attestation": {
150
+ "allowed": true,
151
+ "host_agent_min_confidence": "high",
152
+ "human_allowed": true,
153
+ "evidence_required": [
154
+ {
155
+ "field": "user_switch_flow",
156
+ "description": "Code path showing logout/disconnect before login as a different user.",
157
+ "upload_policy": "upload-with-consent"
158
+ }
159
+ ]
160
+ }
161
+ },
162
+ "symptoms": [
163
+ "NullPointerException",
164
+ "Session conflict",
165
+ "session already exists",
166
+ "cross-user"
167
+ ]
52
168
  },
53
169
  {
54
170
  "id": "ios.auth.logout-on-user-switch",
@@ -56,11 +172,40 @@
56
172
  "title": "iOS must logout before switching user identity",
57
173
  "severity": "warning",
58
174
  "rationale": "When a host app changes user identity, the SDK session must be cleared before the next login. Otherwise posts, push tokens, live objects, and cached state can cross users.",
59
- "applies_when": { "platforms": ["ios"], "outcomes": ["validate-setup"] },
175
+ "applies_when": {
176
+ "platforms": [
177
+ "ios"
178
+ ],
179
+ "outcomes": [
180
+ "validate-setup"
181
+ ]
182
+ },
60
183
  "enforcement": {
61
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "ios.auth.logout-on-user-switch" }],
62
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "user_switch_flow", "description": "Code path showing logout/disconnect before login as a different user.", "upload_policy": "upload-with-consent" }] }
63
- }
184
+ "deterministic": [
185
+ {
186
+ "check": "validator-finding-absent",
187
+ "finding_rule_id": "ios.auth.logout-on-user-switch"
188
+ }
189
+ ],
190
+ "attestation": {
191
+ "allowed": true,
192
+ "host_agent_min_confidence": "high",
193
+ "human_allowed": true,
194
+ "evidence_required": [
195
+ {
196
+ "field": "user_switch_flow",
197
+ "description": "Code path showing logout/disconnect before login as a different user.",
198
+ "upload_policy": "upload-with-consent"
199
+ }
200
+ ]
201
+ }
202
+ },
203
+ "symptoms": [
204
+ "NullPointerException",
205
+ "Session conflict",
206
+ "session already exists",
207
+ "cross-user"
208
+ ]
64
209
  },
65
210
  {
66
211
  "id": "android.auth.no-anonymous-write",
@@ -68,10 +213,33 @@
68
213
  "title": "Android write operations must be gated by authentication",
69
214
  "severity": "warning",
70
215
  "rationale": "Post/comment/message creation should only be reachable after the app knows the current authenticated user and social.plus login has completed. Agents often wire a composer directly to createPost and rely on SDK failure at runtime.",
71
- "applies_when": { "platforms": ["android"], "outcomes": ["validate-setup"] },
216
+ "applies_when": {
217
+ "platforms": [
218
+ "android"
219
+ ],
220
+ "outcomes": [
221
+ "validate-setup"
222
+ ]
223
+ },
72
224
  "enforcement": {
73
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "android.auth.no-anonymous-write" }],
74
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "auth_gate", "description": "Code path showing authentication check before write operations.", "upload_policy": "upload-with-consent" }] }
225
+ "deterministic": [
226
+ {
227
+ "check": "validator-finding-absent",
228
+ "finding_rule_id": "android.auth.no-anonymous-write"
229
+ }
230
+ ],
231
+ "attestation": {
232
+ "allowed": true,
233
+ "host_agent_min_confidence": "high",
234
+ "human_allowed": true,
235
+ "evidence_required": [
236
+ {
237
+ "field": "auth_gate",
238
+ "description": "Code path showing authentication check before write operations.",
239
+ "upload_policy": "upload-with-consent"
240
+ }
241
+ ]
242
+ }
75
243
  }
76
244
  },
77
245
  {
@@ -80,10 +248,33 @@
80
248
  "title": "Flutter write operations must be gated by authentication",
81
249
  "severity": "warning",
82
250
  "rationale": "Post/comment/message creation should only be reachable after the app knows the current authenticated user and social.plus login has completed. Agents often wire a composer directly to createPost and rely on SDK failure at runtime.",
83
- "applies_when": { "platforms": ["flutter"], "outcomes": ["validate-setup"] },
251
+ "applies_when": {
252
+ "platforms": [
253
+ "flutter"
254
+ ],
255
+ "outcomes": [
256
+ "validate-setup"
257
+ ]
258
+ },
84
259
  "enforcement": {
85
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "flutter.auth.no-anonymous-write" }],
86
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "auth_gate", "description": "Code path showing authentication check before write operations.", "upload_policy": "upload-with-consent" }] }
260
+ "deterministic": [
261
+ {
262
+ "check": "validator-finding-absent",
263
+ "finding_rule_id": "flutter.auth.no-anonymous-write"
264
+ }
265
+ ],
266
+ "attestation": {
267
+ "allowed": true,
268
+ "host_agent_min_confidence": "high",
269
+ "human_allowed": true,
270
+ "evidence_required": [
271
+ {
272
+ "field": "auth_gate",
273
+ "description": "Code path showing authentication check before write operations.",
274
+ "upload_policy": "upload-with-consent"
275
+ }
276
+ ]
277
+ }
87
278
  }
88
279
  },
89
280
  {
@@ -92,10 +283,33 @@
92
283
  "title": "TypeScript write operations must be gated by authentication",
93
284
  "severity": "warning",
94
285
  "rationale": "Post/comment/message creation should only be reachable after the app knows the current authenticated user and social.plus login has completed. Agents often wire a composer directly to createPost and rely on SDK failure at runtime.",
95
- "applies_when": { "platforms": ["typescript"], "outcomes": ["validate-setup"] },
286
+ "applies_when": {
287
+ "platforms": [
288
+ "typescript"
289
+ ],
290
+ "outcomes": [
291
+ "validate-setup"
292
+ ]
293
+ },
96
294
  "enforcement": {
97
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "typescript.auth.no-anonymous-write" }],
98
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "auth_gate", "description": "Code path showing authentication check before write operations.", "upload_policy": "upload-with-consent" }] }
295
+ "deterministic": [
296
+ {
297
+ "check": "validator-finding-absent",
298
+ "finding_rule_id": "typescript.auth.no-anonymous-write"
299
+ }
300
+ ],
301
+ "attestation": {
302
+ "allowed": true,
303
+ "host_agent_min_confidence": "high",
304
+ "human_allowed": true,
305
+ "evidence_required": [
306
+ {
307
+ "field": "auth_gate",
308
+ "description": "Code path showing authentication check before write operations.",
309
+ "upload_policy": "upload-with-consent"
310
+ }
311
+ ]
312
+ }
99
313
  }
100
314
  },
101
315
  {
@@ -104,10 +318,33 @@
104
318
  "title": "React Native write operations must be gated by authentication",
105
319
  "severity": "warning",
106
320
  "rationale": "Post/comment/message creation should only be reachable after the app knows the current authenticated user and social.plus login has completed. Agents often wire a composer directly to createPost and rely on SDK failure at runtime.",
107
- "applies_when": { "platforms": ["react-native"], "outcomes": ["validate-setup"] },
321
+ "applies_when": {
322
+ "platforms": [
323
+ "react-native"
324
+ ],
325
+ "outcomes": [
326
+ "validate-setup"
327
+ ]
328
+ },
108
329
  "enforcement": {
109
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "react-native.auth.no-anonymous-write" }],
110
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "auth_gate", "description": "Code path showing authentication check before write operations.", "upload_policy": "upload-with-consent" }] }
330
+ "deterministic": [
331
+ {
332
+ "check": "validator-finding-absent",
333
+ "finding_rule_id": "react-native.auth.no-anonymous-write"
334
+ }
335
+ ],
336
+ "attestation": {
337
+ "allowed": true,
338
+ "host_agent_min_confidence": "high",
339
+ "human_allowed": true,
340
+ "evidence_required": [
341
+ {
342
+ "field": "auth_gate",
343
+ "description": "Code path showing authentication check before write operations.",
344
+ "upload_policy": "upload-with-consent"
345
+ }
346
+ ]
347
+ }
111
348
  }
112
349
  },
113
350
  {
@@ -116,10 +353,33 @@
116
353
  "title": "iOS write operations must be gated by authentication",
117
354
  "severity": "warning",
118
355
  "rationale": "Post/comment/message creation should only be reachable after the app knows the current authenticated user and social.plus login has completed. Agents often wire a composer directly to createPost and rely on SDK failure at runtime.",
119
- "applies_when": { "platforms": ["ios"], "outcomes": ["validate-setup"] },
356
+ "applies_when": {
357
+ "platforms": [
358
+ "ios"
359
+ ],
360
+ "outcomes": [
361
+ "validate-setup"
362
+ ]
363
+ },
120
364
  "enforcement": {
121
- "deterministic": [{ "check": "validator-finding-absent", "finding_rule_id": "ios.auth.no-anonymous-write" }],
122
- "attestation": { "allowed": true, "host_agent_min_confidence": "high", "human_allowed": true, "evidence_required": [{ "field": "auth_gate", "description": "Code path showing authentication check before write operations.", "upload_policy": "upload-with-consent" }] }
365
+ "deterministic": [
366
+ {
367
+ "check": "validator-finding-absent",
368
+ "finding_rule_id": "ios.auth.no-anonymous-write"
369
+ }
370
+ ],
371
+ "attestation": {
372
+ "allowed": true,
373
+ "host_agent_min_confidence": "high",
374
+ "human_allowed": true,
375
+ "evidence_required": [
376
+ {
377
+ "field": "auth_gate",
378
+ "description": "Code path showing authentication check before write operations.",
379
+ "upload_policy": "upload-with-consent"
380
+ }
381
+ ]
382
+ }
123
383
  }
124
384
  }
125
385
  ]