@amityco/social-plus-vise 1.8.0 → 1.8.1

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 CHANGED
@@ -4,6 +4,23 @@ All notable changes to `@amityco/social-plus-vise` are documented in this file.
4
4
 
5
5
  The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.8.1 — 2026-07-11
8
+
9
+ ### Added
10
+ - **Complete React Native journey proof.** The deterministic harness now runs the full governed feed journey on TypeScript, React Native, Android, Flutter, and iOS, including every documented exit state, scope-omit recovery, attestation and source-fingerprint lifecycle, contract drift, sensors, and runtime-evidence handoff.
11
+ - **Public-preview measurement contract.** A schema-validated, metadata-only report template defines the SDK custom-UI, UIKit quick-launch, and brownfield/day-2 evaluation tracks while forbidding source upload, recorded secrets, and customer identifiers. Structural, fresh-run, and external-adoption evidence remain explicitly separate.
12
+
13
+ ### Changed
14
+ - **Locked UIKit plans stay on UIKit.** After `solution_path=uikit` is explicitly selected, the plan keeps an actionable UIKit installation/provider/customization step and omits contradictory direct-SDK outcome steps. Hybrid plans retain those steps only for differentiated behavior outside UIKit.
15
+ - **Explicit workplan surfaces are preserved.** An accepted Engagement Intelligence variant may add coherent companion surfaces, but it can no longer erase surfaces named directly by the user; bare plural `communities` is recognized in multi-surface requests.
16
+
17
+ ### Fixed
18
+ - **Safe placeholders no longer mask later hardcoded values.** Literal scanning continues past documented placeholder IDs and API keys, including later matches in the same file or project, so a benign env fallback cannot hide a real committed secret or target.
19
+ - **Public contract wording.** The npm README now reflects the Node.js 20 minimum, fail-closed scan coverage, strict sensor selection, and the `vise check --ci` all-engagement release gate.
20
+
21
+ ### Compatibility
22
+ - This patch changes no sidecar schema or exit code. SDK plans and unresolved UIKit decisions retain their existing branch visibility; only an explicitly locked UIKit route suppresses direct-SDK outcome steps.
23
+
7
24
  ## 1.8.0 — 2026-07-10
8
25
 
9
26
  ### Added
package/README.md CHANGED
@@ -66,6 +66,8 @@ It ships as three layers:
66
66
 
67
67
  > **New to social.plus?** Vise integrates the social.plus SDK. To adopt social.plus, [talk to our team](https://www.social.plus/contact/contact-sales?utm_source=vise&utm_medium=npm-readme&utm_campaign=ga). Existing customers get their **API key + region** from the [social.plus Console](https://console.socialplus.com) and can follow the [SDK getting-started guide](https://learn.social.plus/social-plus-sdk/getting-started/overview). You can install Vise and explore `vise inspect`/`vise plan` before you have credentials.
68
68
 
69
+ > **Runtime requirement:** Vise requires Node.js 20 or newer. Run `node --version` before installing if you are unsure which runtime your terminal or CI environment uses.
70
+
69
71
  ```sh
70
72
  # 1. Install the CLI
71
73
  npm install -g @amityco/social-plus-vise
@@ -202,7 +204,7 @@ Run `vise <command> --help` for full flags. JSON output is the default for agent
202
204
  |---|---|
203
205
  | `vise explore "<request>"` | Pre-credentials discovery: map a request to what social.plus offers (candidate outcome, capabilities, docs, next command). No project or API key needed |
204
206
  | `vise doctor` | Verify install; print version, install path, docs source |
205
- | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors |
207
+ | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors, and bounded scan coverage. An incomplete scan cannot initialize or produce a green check; select a concrete app surface in a large monorepo |
206
208
  | `vise plan [path] --request "..." [--summary]` | Grounded implementation plan with intake questions and docs citations; `--summary` prints a compact route/intake view |
207
209
  | `vise plan --summary "..."` | Shortcut for quick routing or discovery when the current directory is the repo |
208
210
  | `vise plan-harness [path] --request "..."` | Pre-planning step: build the harness around the request |
@@ -253,7 +255,7 @@ Everything in this group is local and advisory: no uploads, no `vise check` exit
253
255
  | Command | Purpose |
254
256
  |---|---|
255
257
  | `vise check [path]` | Re-validate against the recorded contract: `green`, `needs-attestation`, `deterministic-failures`, `blocked`, `contract-drift`, `completeness-gap`, `selected-capability-failures`, `no-platform` (no SDK platform detected — reported as a failure, not a vacuous green), or `runtime-proof-waived` |
256
- | `vise check [path] --ci` | Read-only variant that exits non-zero unless green (for CI) |
258
+ | `vise check [path] --ci` | Read-only release gate for the active contract **and every recorded engagement**. Active non-green keeps its normal exit code; active green with completed-work drift exits `11` |
257
259
  | `vise check [path] --allow-proof-waiver` | Accept an honest `runtime-proof-waived` result (from `vise smoke waive`) as passing (exit 0) instead of blocking; without it a waiver blocks CI by default |
258
260
  | `vise check [path] --new-only` | **Brownfield gate.** With a recorded baseline, gate only on rule findings introduced *since* the baseline (pre-existing ones are reported but excluded). The outcome's completeness checklist and selected optional capabilities are **never** excluded — they are the engagement's deliverables and gate like a default check. Default `check` always gates on everything |
259
261
  | `vise check [path] --engagement <outcome>` | **Re-verdict one recorded engagement** (from `sp-vise/engagements/`) against the current ruleset and code. Exit `11` on drift, `0` when it still holds; never the active engagement's codes. `ruleset_upgraded` discloses when the installed ruleset moved since it was recorded |
@@ -269,7 +271,7 @@ Everything in this group is local and advisory: no uploads, no `vise check` exit
269
271
 
270
272
  | Command | Purpose |
271
273
  |---|---|
272
- | `vise run-sensors [path] [--dry-run]` | Run detected project scripts (npm, Gradle, Flutter, lint, typecheck, SDK smokes); `--dry-run` lists without executing |
274
+ | `vise run-sensors [path] [--dry-run] [--include "<sensor name>"]` | Run detected project scripts (npm, Gradle, Flutter, lint, typecheck, SDK smokes); `--dry-run` lists without executing. Every explicit include must match a detected sensor or the command fails; output is bounded and timed-out descendants are terminated |
273
275
  | `vise smoke [path] --log <file>` | Assess a captured mount-smoke log into a pass/fail verdict + record evidence. `expect: "populated"` requires `loaded count=N` with `N > 0`; this catches session-establish and empty-data gaps that static checks cannot see |
274
276
  | `vise smoke waive [path] --reason "..." [--mode declined\|deviceless]` | Record an auditable runtime-proof waiver when proof is declined or no device/emulator is available, instead of faking evidence. `vise check` then reports `runtime-proof-waived` (exit 9; accept with `--allow-proof-waiver`) rather than a red-forever live surface |
275
277
  | `vise install-skill --target <host>` | Install the bundled skill into a coding host (see [Quick Start](#quick-start)) |
@@ -321,7 +323,7 @@ jobs:
321
323
  - run: vise check . --ci
322
324
  ```
323
325
 
324
- `vise check --ci` is read-only — it never updates `sp-vise/` — and its JSON output includes a structured `ci` block for pipeline logs.
326
+ `vise check --ci` is read-only — it never updates `sp-vise/` — and its JSON output includes a structured `ci` block for pipeline logs. It automatically re-verdicts every recorded engagement, so CI holds previously completed surfaces green without a second `--all-engagements` command.
325
327
 
326
328
  | Exit code | Meaning |
327
329
  |---|---|
@@ -334,7 +336,7 @@ jobs:
334
336
  | `6` | An explicitly selected optional capability failed its source sensors |
335
337
  | `8` | No SDK platform detected (`no-platform`) — reported as a failure, never a vacuous green |
336
338
  | `9` | Runtime proof waived (`runtime-proof-waived`) — accept explicitly with `--allow-proof-waiver` |
337
- | `11` | Engagement drift (`--engagement` / `--all-engagements` only): the active gate is green but a recorded engagement re-verdicts non-green — a previously finished surface no longer holds |
339
+ | `11` | Engagement drift (`--ci`, `--engagement`, or `--all-engagements`): the active gate is green but a recorded engagement re-verdicts non-green — a previously finished surface no longer holds |
338
340
 
339
341
  ## Compliance Contract
340
342
 
package/dist/server.js CHANGED
@@ -1595,7 +1595,12 @@ async function activeBlueprint(repoRoot, request, plan) {
1595
1595
  const planSequence = socialWorkplanSequence(plan);
1596
1596
  const flowFallback = await socialWorkplanSequenceFromFlow(repoRoot, request);
1597
1597
  const canonicalRequest = flowFallback?.request ?? request;
1598
- const full = planSequence.length ? planSequence : flowFallback?.sequence ?? [];
1598
+ const isCanonicalRequest = normalizeWorkflowRequest(request) === normalizeWorkflowRequest(canonicalRequest);
1599
+ const full = flowFallback && !isCanonicalRequest
1600
+ ? flowFallback.sequence
1601
+ : planSequence.length
1602
+ ? planSequence
1603
+ : flowFallback?.sequence ?? [];
1599
1604
  const omitted = await readOmittedSurfaces(repoRoot, canonicalRequest);
1600
1605
  const omittedIds = new Set(omitted.map((item) => item.id));
1601
1606
  return { sequence: full.filter((surface) => !omittedIds.has(surface.id)), omitted, canonicalRequest };
@@ -218,7 +218,7 @@ async function buildIntegrationPlan(repoPath, request, surfacePath, answers = {}
218
218
  ...solutionPathImplementationSteps(solutionPath),
219
219
  ...uikitCustomizationImplementationSteps(uikitCustomization),
220
220
  ...crossPlatformHandoffSteps,
221
- ...definition.implementationSteps(ctx),
221
+ ...outcomeImplementationStepsForSolutionPath(solutionPath, definition.implementationSteps(ctx)),
222
222
  ],
223
223
  validation: ["validate_setup", "run_sensors", ...definition.validation(platform)],
224
224
  ...(clarify ? { clarify } : {}),
@@ -442,18 +442,30 @@ function solutionPathDecisions(solutionPath, answers) {
442
442
  ];
443
443
  }
444
444
  function solutionPathImplementationSteps(solutionPath) {
445
- if (!solutionPath.decision.requiredBeforeHandRolledUi) {
445
+ const unresolved = solutionPath.decision.requiredBeforeHandRolledUi;
446
+ if (solutionPath.recommendation === "sdk" && !unresolved) {
446
447
  return [];
447
448
  }
449
+ const step = solutionPath.recommendation === "uikit"
450
+ ? unresolved
451
+ ? "Confirm the social.plus UIKit path before building standard social UI from SDK primitives; use UIKit install/auth/customization docs as the primary implementation source. The SDK feed/chat/profile steps that follow apply ONLY to the direct-SDK (or hybrid SDK-side) route — do not hand-roll the standard surface from them when the customer is using UIKit."
452
+ : "Implement the explicitly selected social.plus UIKit path as the primary artifact: follow UIKit installation, authentication, provider/page, behavior, and customization guidance. Direct-SDK outcome steps are intentionally omitted for this locked UIKit route; do not hand-roll the standard social surface from SDK primitives."
453
+ : solutionPath.recommendation === "hybrid" && !unresolved
454
+ ? "Implement the explicitly selected hybrid path: use UIKit for standard social surfaces, and apply the direct-SDK outcome steps only to the differentiated app-specific behavior that UIKit does not cover. Do not rebuild UIKit-covered surfaces from SDK primitives."
455
+ : "Resolve whether this is a UIKit, SDK, or hybrid build before starting UI implementation; the SDK implementation steps that follow apply to the SDK/hybrid route, not to a UIKit build. Do not let mixed speed/customization signals collapse into a hand-rolled SDK UI by default.";
448
456
  return [
449
457
  {
450
- step: solutionPath.recommendation === "uikit"
451
- ? "Confirm the social.plus UIKit path before building standard social UI from SDK primitives; use UIKit install/auth/customization docs as the primary implementation source. The SDK feed/chat/profile steps that follow apply ONLY to the direct-SDK (or hybrid SDK-side) route — do not hand-roll the standard surface from them when the customer is using UIKit."
452
- : "Resolve whether this is a UIKit, SDK, or hybrid build before starting UI implementation; the SDK implementation steps that follow apply to the SDK/hybrid route, not to a UIKit build. Do not let mixed speed/customization signals collapse into a hand-rolled SDK UI by default.",
458
+ step,
453
459
  evidence: ["solutionPath", ...solutionPath.evidence],
454
460
  },
455
461
  ];
456
462
  }
463
+ function outcomeImplementationStepsForSolutionPath(solutionPath, outcomeSteps) {
464
+ if (solutionPath.recommendation === "uikit" && !solutionPath.decision.requiredBeforeHandRolledUi) {
465
+ return [];
466
+ }
467
+ return outcomeSteps;
468
+ }
457
469
  function solutionPathImplementationRules(solutionPath) {
458
470
  if (solutionPath.recommendation === "sdk" && !solutionPath.decision.requiredBeforeHandRolledUi) {
459
471
  return [];
@@ -719,7 +731,11 @@ const SOCIAL_SURFACE_SEQUENCE = [
719
731
  id: "community",
720
732
  outcome: "add-community",
721
733
  label: "Community management",
722
- aliases: [/\b(create|manage|join|leave|membership|members?|roles?|invitations?|categories?)\s+(?:a\s+|the\s+)?communit/i, /\bcommunity\s+(creation|management|members?|roles?|invitations?|categories|settings|moderation)\b/i],
734
+ aliases: [
735
+ /\bcommunities\b/i,
736
+ /\b(create|manage|join|leave|membership|members?|roles?|invitations?|categories?)\s+(?:a\s+|the\s+)?communit/i,
737
+ /\bcommunity\s+(creation|management|members?|roles?|invitations?|categories|settings|moderation)\b/i,
738
+ ],
723
739
  },
724
740
  {
725
741
  id: "notifications",
@@ -743,6 +759,13 @@ async function socialWorkplanFor(args) {
743
759
  const surface = SOCIAL_SURFACE_SEQUENCE.find((item) => item.id === hint.id);
744
760
  return surface ? [{ surface, creativeHint: hint }] : [];
745
761
  });
762
+ const selectedIds = new Set(selected.map((entry) => entry.surface.id));
763
+ for (const surface of matched) {
764
+ if (!selectedIds.has(surface.id)) {
765
+ selected.push({ surface });
766
+ selectedIds.add(surface.id);
767
+ }
768
+ }
746
769
  if (selected.length <= 1) {
747
770
  return undefined;
748
771
  }
@@ -2966,11 +2966,19 @@ function firstLiteralAssignment(contents, patterns, platform) {
2966
2966
  for (const [file, rawContent] of contents) {
2967
2967
  const content = commentStripped(file, platform, rawContent);
2968
2968
  for (const pattern of patterns) {
2969
- pattern.lastIndex = 0;
2970
- const match = pattern.exec(content);
2971
- if (match?.[1]) {
2969
+ const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
2970
+ const matcher = new RegExp(pattern.source, flags);
2971
+ let match;
2972
+ while ((match = matcher.exec(content)) !== null) {
2973
+ if (!match[1]) {
2974
+ continue;
2975
+ }
2976
+ const value = match[1].trim();
2977
+ if (isAllowedPlaceholder(value)) {
2978
+ continue;
2979
+ }
2972
2980
  const name = match[0].split(/[=:]/)[0]?.trim() ?? "literal";
2973
- return { file, name, value: match[1].trim() };
2981
+ return { file, name, value };
2974
2982
  }
2975
2983
  }
2976
2984
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amityco/social-plus-vise",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Skill-guided deterministic CLI for social.plus SDK integration assistance.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",