@amityco/social-plus-vise 0.14.22 → 0.14.24

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,26 @@ 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
+ ## 0.14.24 — 2026-06-06
8
+
9
+ ### Changed
10
+ - **Evidence-backed workplan completion:** `vise workplan complete` now runs the current compliance check, refuses non-green results, records check evidence in `sp-vise/workplan.json`, and snapshots the active compliance sidecar under `sp-vise/workplan-snapshots/<surface>/`.
11
+ - **Scoped comments/chat/profile completeness:** the `add-comments` baseline is now narrowed to the comment composer/write affordance, `add-chat` is narrowed to send plus read/unread state, and `add-follow` is narrowed to follower/following relationship data, so focused surfaces do not fail on unselected replies, mentions, media-message types, edit/delete, typing, follow-request, block-list, or deeper moderation scope.
12
+ - **Bundled host-agent skill guidance:** `social-plus-vise` now teaches agents to drive broad social requests through `vise workplan next/status/complete`, implement one focused surface at a time, and show `sp-vise/design-preview.html` before answering `design_contract_confirmation=yes`.
13
+
14
+ ### Verified
15
+ - CLI regression coverage now initializes a focused Android feed sidecar before marking a workplan surface complete, then verifies the green-check evidence and per-surface snapshot files.
16
+ - Capability regression coverage now locks the narrow `add-comments`, `add-chat`, and `add-follow` baselines.
17
+
18
+ ## 0.14.23 — 2026-06-06
19
+
20
+ ### Added
21
+ - **Workplan orchestration CLI:** `vise workplan next`, `vise workplan status`, and `vise workplan complete` coordinate broad social requests across feed, comments, chat, and profile surfaces while recording local progress in `sp-vise/workplan.json`.
22
+
23
+ ### Verified
24
+ - CLI regression coverage now locks next-surface selection, completion recording, and feed-to-comments progression for broad Android social workplans.
25
+ - Dogfooded `0.14.22` on `/Users/admin/Documents/music-player-android`: refreshed the feed contract, reached `vise check` green with 43/43 deterministic passes, synced attestations, and passed Gradle assemble/unit-test sensors.
26
+
7
27
  ## 0.14.22 — 2026-06-05
8
28
 
9
29
  ### Added
package/README.md CHANGED
@@ -77,7 +77,7 @@ Vise validates on three layers, and the layer is set by the *kind of claim* —
77
77
  |---|---|---|---|
78
78
  | **SDK compliance** | "this is **wrong**" | 300+ deterministic rules (session renewal, live-collection vs one-shot, no secret in logs, parent-child rendering, ban-state gating…) | **Hard gate** — `vise check` blocks until green or attested. A small advisory subset surfaces as informational only and never blocks. |
79
79
  | **Design conformance** | "this **looks off**" | extract the customer's design system into a contract, render a preview for confirmation, then check token usage | **Advisory** — `vise design check`/`preview`; never fails a build |
80
- | **Feature completeness** | "this is **missing**" | Vise proposes a narrow baseline per outcome; for add-feed, pagination is mandatory, while richer feed capabilities are opt-in choices from `vise plan` | **Decision gate** — `vise check` exits `completeness-gap` until each baseline capability is built or validly opted out; selected optional capabilities run separate sensors |
80
+ | **Feature completeness** | "this is **missing**" | Vise proposes a narrow baseline per outcome; for add-feed, pagination is mandatory, for add-comments, the composer/write affordance is mandatory, for add-chat, send plus read/unread state are mandatory, and for add-follow/profile, SDK-backed follower/following data is mandatory, while richer feed capabilities are opt-in choices from `vise plan` | **Decision gate** — `vise check` exits `completeness-gap` until each baseline capability is built or validly opted out; selected optional capabilities run separate sensors |
81
81
 
82
82
  Correctness is gated by deterministic rules or attestations. Baseline completeness is gated by explicit scope decisions: if a baseline capability is legitimately out of scope, record `// vise: scope-omit <id> — <reason>` and it no longer blocks. Optional feed capabilities such as image upload, poll creation, and edit post are offered during planning and become checked only after the user opts in. Conformance remains advisory because "matches the brand" is legitimately subjective. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
83
83
 
@@ -161,14 +161,15 @@ Aggregate: **98/99 expected feed capabilities** and **27/27 selected optional ca
161
161
 
162
162
  ### Current Release Validation
163
163
 
164
- Version 0.14.22 carries current release proof around the full feed-forward, product-expectation, and validation flow:
164
+ Version 0.14.24 carries current release proof around the full feed-forward, product-expectation, and validation flow:
165
165
 
166
166
  | Surface | What was validated |
167
167
  |---|---|
168
168
  | **Product flow** | Local end-to-end smoke covers design extraction, plan feed-forward, blocking intake, answered init, capability check, design conformance, and sensor discovery. |
169
- | **Multi-surface planning** | Broad social requests are decomposed into a `socialWorkplan` sequence for feed, comments, chat, and profile work instead of forcing a single top-level surface choice. Each surface includes focused `vise plan` / `vise init` commands for the host agent to run when implementing that slice. |
169
+ | **Multi-surface planning** | Broad social requests are decomposed into a `socialWorkplan` sequence for feed, comments, chat, and profile work instead of forcing a single top-level surface choice. `vise workplan next` tells the host agent which surface to implement next, and `vise workplan complete` records green-check progress in `sp-vise/workplan.json` with per-surface snapshots under `sp-vise/workplan-snapshots/<surface>/`. |
170
170
  | **Plan questions** | Plans surface blocking questions such as `design_contract_confirmation`, product-scope questions such as `feed_post_type_scope`, `feed_composer_type_scope`, `comment_tray_scope`, `chat_inbox_scope`, and `profile_identity_scope`, plus optional choices such as `feed_optional_capabilities`. Focused plans still accept `feature_surface` answers when the agent is ready to implement one surface. |
171
171
  | **Capability-to-sensor flow** | Vise checks platform support, matches the prompt to available capabilities, offers supported features as questions, records answers, and turns selected answers into sensors in `vise check`. |
172
+ | **Android workplan dogfood** | A brownfield Android music-player app refreshed under `0.14.24` reached `vise check` green with **43/43 deterministic passes** on the focused feed surface and recorded a green-check workplan snapshot. This is dogfood evidence, not a controlled multi-agent benchmark. |
172
173
  | **Shared product expectations** | Public IDs such as `feed.target-resolved`, `feed.post-type-scope-explicit`, `comments.creation-affordance`, `chat.channel-list-order-explicit`, `community.avatar-from-sdk`, `moderation.role-gated-action`, `follow.relationship-live`, `profile.identity-from-sdk`, `profile.social-counts`, and `notifications.tray-live` stay platform-agnostic while check results retain concrete `contractRuleId` and `validator.sensorId` evidence when deterministic sensors exist. |
173
174
  | **Rule detection** | TP-track dashboard detects **321/321 seeded rule gaps (100.0%)** in the static corpus. |
174
175
  | **Packed-package smoke** | Packed-package and host-agent smokes exercise the release tarball path, surfaced plan questions, selected optional capability sensors, rejected design confirmation handling, and exact contract-rule evidence for shared product expectations. |
@@ -299,6 +300,9 @@ The flow above is what the skill teaches your AI agent. You — the human — dr
299
300
  | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors |
300
301
  | `vise plan [path] --request "..."` | Produce a grounded implementation plan with intake questions and docs citations |
301
302
  | `vise plan-harness [path] --request "..."` | (Pre-planning step) Build the harness around the request |
303
+ | `vise workplan next [path] --request "..."` | For broad social requests, print the next uncompleted surface plus focused `plan` / `init` / verification commands |
304
+ | `vise workplan status [path] --request "..."` | Show the broad social workplan sequence and which surfaces are already marked complete |
305
+ | `vise workplan complete [path] --request "..." --surface <id>` | Record a completed workplan surface after green `vise check`; writes progress to `sp-vise/workplan.json` and snapshots the active sidecar under `sp-vise/workplan-snapshots/<surface>/` |
302
306
  | `vise init [path] --request "..." [--answer key=value]` | Write the `sp-vise/` compliance contract after blocking intake is answered; returns `needs-clarification` and exits 7 if required answers are missing |
303
307
  | `vise blocks list --registry <path>` | Read a social.plus Block Factory registry |
304
308
  | `vise blocks plan [path] --block <id> --registry <path>` | Plan safe block package, source-anchor, sidecar, and sensor changes |
@@ -454,6 +458,8 @@ After a successful `vise init`, your project gets a `sp-vise/` directory. If ini
454
458
  | `sp-vise/intake.json` | `vise init` | The request, outcome, intake answers, remaining blocking count, design-review status (`absent`, `needs-confirmation`, `accepted`, or `rejected`), and any retrospective `--allow-unresolved-intake` acknowledgement. |
455
459
  | `sp-vise/attestations/*.json` | `vise sync` (deterministic) or `vise attest` (host-agent / human) | Per-rule evidence: signer, confidence, rationale, cited files (with source fingerprints for drift detection). |
456
460
  | `sp-vise/inspection.json` | `vise init` | The platform, monorepo surface, and design-token signals detected at init time. |
461
+ | `sp-vise/workplan.json` | `vise workplan complete` | Local progress for broad social workplans: request, completed surface IDs, outcomes, timestamps, green-check evidence, snapshot paths, and optional host-agent notes. |
462
+ | `sp-vise/workplan-snapshots/<surface>/` | `vise workplan complete` | Per-surface compliance/check snapshots copied from the active sidecar so later focused surfaces can overwrite `sp-vise/compliance.json` without erasing earlier proof. |
457
463
  | `sp-vise/design-contract.json` | `vise design extract` | The extracted design contract: declared tokens, breakpoints, advisory components, source file digests (for freshness detection), and a stable digest over design facts. |
458
464
  | `sp-vise/design-preview.html` | `vise design extract` or `vise design preview` | Self-contained visual review of the design contract, embedded prototype when available, token swatches, and design-check conformance summary. Open this before answering `design_contract_confirmation`. |
459
465
  | `sp-vise/design-reference.html` | `vise design reference` | Self-contained HTML design-system spec (token swatches, type samples, components). Human/VLM-readable; open in a browser alongside the app. |
@@ -13,7 +13,11 @@
13
13
  * agent subtracts with justification — it doesn't have to remember the set.
14
14
  *
15
15
  * Baseline capability gates must stay narrow. For add-feed, pagination is the
16
- * mandatory capability; richer composer affordances are selected optional sensors.
16
+ * mandatory capability. For add-comments, the comment composer is mandatory.
17
+ * For add-chat, sending plus read/unread state are mandatory. For add-follow,
18
+ * follower/following relationship data is mandatory. Replies, mentions,
19
+ * edit/delete, typing, media-message types, block lists, and deeper moderation
20
+ * scope are scoped separately. Richer composer affordances are selected optional sensors.
17
21
  */
18
22
  import { readdir, readFile, stat } from "node:fs/promises";
19
23
  import path from "node:path";
@@ -325,10 +329,10 @@ export const CAPABILITIES = [
325
329
  },
326
330
  {
327
331
  id: "followers-following",
328
- label: "Follower / following lists",
332
+ label: "Follower / following counts or lists",
329
333
  outcomes: ["add-follow"],
330
- symbols: [/getFollowers/i, /getFollowings/i, /getFollowerList/i, /followRelationship/i],
331
- hint: "list/observe followers and following as a Live Collection (getFollowers/getFollowings)",
334
+ symbols: [/getFollowers/i, /getFollowings/i, /getFollowerList/i, /followRelationship/i, /getMyFollowInfo/i, /getFollowerCount/i, /getFollowingCount/i],
335
+ hint: "source follower/following counts or lists from SDK relationship APIs, not placeholders",
332
336
  },
333
337
  {
334
338
  id: "follow-status",
@@ -1166,6 +1170,9 @@ function symbolHaystack(symbols) {
1166
1170
  const ADVISORY_NOTE = "Build each missing capability, or opt out with a recorded reason: `// vise: scope-omit <id> — <reason>`. A scope-omit marker without a reason is invalid and still counts as missing. Missing capabilities that are neither built nor validly opted-out cause `vise check` to exit with status `completeness-gap` (exit code 5).";
1167
1171
  const BASELINE_CAPABILITY_IDS_BY_OUTCOME = {
1168
1172
  "add-feed": ["pagination"],
1173
+ "add-comments": ["comment-composer"],
1174
+ "add-chat": ["send-message", "read-state"],
1175
+ "add-follow": ["followers-following"],
1169
1176
  };
1170
1177
  function baselineCapabilities(outcome) {
1171
1178
  const caps = CAPABILITIES.filter((c) => c.outcomes.includes(outcome));
package/dist/server.js CHANGED
@@ -177,6 +177,23 @@ async function handleCli(args) {
177
177
  });
178
178
  return "exit";
179
179
  }
180
+ if (command === "workplan") {
181
+ const sub = args[1];
182
+ const subArgs = args.slice(2);
183
+ if (sub === "next" || sub === "status") {
184
+ assertOnlyKnownFlags(subArgs, ["request", "surface", "surface-path", "answer"], `workplan ${sub}`);
185
+ console.log(JSON.stringify(await workplanStatus(subArgs), null, 2));
186
+ return "exit";
187
+ }
188
+ if (sub === "complete") {
189
+ assertOnlyKnownFlags(subArgs, ["request", "surface", "surface-path", "answer", "note"], "workplan complete");
190
+ console.log(JSON.stringify(await completeWorkplanSurface(subArgs), null, 2));
191
+ return "exit";
192
+ }
193
+ console.error(`Unknown workplan subcommand: ${sub ?? "(none)"}. Expected "next", "status", or "complete".`);
194
+ process.exitCode = 1;
195
+ return "exit";
196
+ }
180
197
  if (command === "validate" || command === "validate-setup") {
181
198
  await printToolResult(validateSetupTool, {
182
199
  repoPath: positionalRepoPath(args.slice(1)),
@@ -442,6 +459,22 @@ Create a harness guide/sensor plan for the requested SDK integration.
442
459
  Usage:
443
460
  vise plan-harness [repoPath] --request "Add a social feed"
444
461
  vise plan-harness apps/web --request "Set up notifications" --surface apps/web`;
462
+ }
463
+ if (command === "workplan") {
464
+ return `${packageName} workplan
465
+
466
+ Coordinate broad social requests across the ordered per-surface plan.
467
+
468
+ Usage:
469
+ vise workplan next [repoPath] --request "Add feed, comments, chat, and profile"
470
+ vise workplan status [repoPath] --request "Add feed, comments, chat, and profile"
471
+ vise workplan complete [repoPath] --request "Add feed, comments, chat, and profile" --surface feed
472
+
473
+ Notes:
474
+ next/status re-run the broad plan, ignore any feature_surface answer, and combine it
475
+ with sp-vise/workplan.json progress. complete records a host-agent progress marker;
476
+ run vise check, vise sync, vise validate, and vise run-sensors before marking a
477
+ surface complete.`;
445
478
  }
446
479
  if (command === "search-docs" || command === "search_docs") {
447
480
  return `${packageName} search-docs
@@ -666,6 +699,7 @@ Usage:
666
699
  vise inspect [repoPath] Inspect platform and design signals
667
700
  vise debug [repoPath] --error ... Debug an SDK-specific runtime error and emit a repair brief
668
701
  vise plan [repoPath] --request "..." Create an implementation plan
702
+ vise workplan next [repoPath] --request "..." Get the next broad-social surface to implement
669
703
  vise init [repoPath] --request "..." Initialize compliance sidecar
670
704
  vise check [repoPath] Check compliance contract
671
705
  vise sync [repoPath] Persist deterministic-pass evidence
@@ -916,6 +950,215 @@ function assertOnlyKnownFlags(args, allowed, commandName) {
916
950
  const allowedList = allowed.length > 0 ? `Allowed flags: ${allowed.map((flag) => `--${flag}`).join(", ")}.` : "This command takes no flags.";
917
951
  throw new Error(`${commandName} does not accept ${unknown.join(", ")}. ${allowedList} Run \`vise ${commandName} --help\` for usage.`);
918
952
  }
953
+ async function workplanStatus(args) {
954
+ const repoRoot = path.resolve(positionalRepoPath(args));
955
+ const request = requiredFlagValue(args, "request", "workplan next/status requires --request.");
956
+ const plan = await workplanPlan(repoRoot, request, args);
957
+ const sequence = socialWorkplanSequence(plan);
958
+ const progress = await readWorkplanProgress(repoRoot);
959
+ const completed = progress?.request === request ? progress.completed : [];
960
+ const completedIds = new Set(completed.map((item) => item.surface));
961
+ const nextSurface = sequence.find((surface) => !completedIds.has(surface.id));
962
+ if (sequence.length === 0) {
963
+ return {
964
+ status: "not-applicable",
965
+ reason: "The request did not produce a multi-surface social workplan. Use `vise plan` for the single-outcome flow.",
966
+ request,
967
+ progressPath: workplanProgressPath(repoRoot),
968
+ };
969
+ }
970
+ return {
971
+ status: nextSurface ? "next-surface" : "complete",
972
+ request,
973
+ progressPath: workplanProgressPath(repoRoot),
974
+ completed,
975
+ sequence: sequence.map((surface) => ({
976
+ id: surface.id,
977
+ order: surface.order,
978
+ outcome: surface.outcome,
979
+ label: surface.label,
980
+ completed: completedIds.has(surface.id),
981
+ })),
982
+ nextSurface: nextSurface
983
+ ? {
984
+ id: nextSurface.id,
985
+ order: nextSurface.order,
986
+ outcome: nextSurface.outcome,
987
+ label: nextSurface.label,
988
+ intake: nextSurface.intake,
989
+ validation: nextSurface.validation,
990
+ commands: {
991
+ plan: nextSurface.planCommand,
992
+ init: nextSurface.initCommand,
993
+ check: "vise check .",
994
+ sync: "vise sync .",
995
+ validate: "vise validate .",
996
+ sensors: "vise run-sensors .",
997
+ complete: `vise workplan complete . --request ${shellQuoteForCommand(request)} --surface ${nextSurface.id}`,
998
+ },
999
+ }
1000
+ : undefined,
1001
+ nextStep: nextSurface
1002
+ ? `Resolve the ${nextSurface.id} surface intake, run its focused plan/init command, implement it, then run check/sync/validate/sensors before marking it complete.`
1003
+ : "All workplan surfaces are marked complete. Run one final `vise check .`, `vise validate .`, and `vise run-sensors .` before handoff.",
1004
+ };
1005
+ }
1006
+ async function completeWorkplanSurface(args) {
1007
+ const repoRoot = path.resolve(positionalRepoPath(args));
1008
+ const request = requiredFlagValue(args, "request", "workplan complete requires --request.");
1009
+ const surfaceId = requiredFlagValue(args, "surface", "workplan complete requires --surface <surface-id>.");
1010
+ const note = flagValue(args, "note");
1011
+ const plan = await workplanPlan(repoRoot, request, args);
1012
+ const sequence = socialWorkplanSequence(plan);
1013
+ const surface = sequence.find((item) => item.id === surfaceId);
1014
+ if (!surface) {
1015
+ throw new Error(`Surface "${surfaceId}" is not in this social workplan. Available surfaces: ${sequence.map((item) => item.id).join(", ") || "(none)"}.`);
1016
+ }
1017
+ const now = new Date().toISOString();
1018
+ const check = await greenWorkplanCheck(repoRoot, surfaceId);
1019
+ const snapshot = await writeWorkplanSurfaceSnapshot(repoRoot, surfaceId, check, now);
1020
+ const existing = await readWorkplanProgress(repoRoot);
1021
+ const base = existing?.request === request
1022
+ ? existing
1023
+ : {
1024
+ schema_version: 2,
1025
+ vise_version: packageVersion,
1026
+ request,
1027
+ generated_at: now,
1028
+ updated_at: now,
1029
+ completed: [],
1030
+ };
1031
+ const withoutSurface = base.completed.filter((item) => item.surface !== surfaceId);
1032
+ const progress = {
1033
+ ...base,
1034
+ schema_version: 2,
1035
+ vise_version: packageVersion,
1036
+ updated_at: now,
1037
+ completed: [
1038
+ ...withoutSurface,
1039
+ {
1040
+ surface: surfaceId,
1041
+ outcome: surface.outcome,
1042
+ completed_at: now,
1043
+ ...(note ? { note } : {}),
1044
+ evidence: {
1045
+ check: {
1046
+ checked_at: now,
1047
+ status: check.status,
1048
+ exit_code: check.exitCode,
1049
+ outcome: check.outcome,
1050
+ ...(check.surfacePath ? { surface_path: check.surfacePath } : {}),
1051
+ summary: check.summary,
1052
+ },
1053
+ snapshot,
1054
+ },
1055
+ },
1056
+ ].sort((a, b) => sequenceIndex(sequence, a.surface) - sequenceIndex(sequence, b.surface)),
1057
+ };
1058
+ await writeWorkplanProgress(repoRoot, progress);
1059
+ const status = await workplanStatus(args);
1060
+ return {
1061
+ status: "recorded",
1062
+ progressPath: workplanProgressPath(repoRoot),
1063
+ recorded: progress.completed.find((item) => item.surface === surfaceId),
1064
+ nextSurface: status.nextSurface,
1065
+ nextStep: status.nextStep,
1066
+ };
1067
+ }
1068
+ async function greenWorkplanCheck(repoRoot, surfaceId) {
1069
+ let check;
1070
+ try {
1071
+ check = await checkCompliance(repoRoot);
1072
+ }
1073
+ catch (error) {
1074
+ const message = error instanceof Error ? error.message : String(error);
1075
+ throw new Error(`Cannot mark "${surfaceId}" complete because the current compliance check could not run. Run the focused \`vise init\` command for this surface first, then \`vise check .\`. ${message}`);
1076
+ }
1077
+ if (check.status !== "green") {
1078
+ throw new Error(`Cannot mark "${surfaceId}" complete because \`vise check\` returned "${check.status}" (exit ${check.exitCode}). Resolve the check result before recording workplan progress.`);
1079
+ }
1080
+ return check;
1081
+ }
1082
+ async function writeWorkplanSurfaceSnapshot(repoRoot, surfaceId, check, snapshotAt) {
1083
+ const snapshotDir = workplanSurfaceSnapshotDir(repoRoot, surfaceId);
1084
+ await mkdir(snapshotDir, { recursive: true });
1085
+ const files = [];
1086
+ const sidecarRoot = sidecarPath(repoRoot);
1087
+ for (const fileName of ["compliance.json", "intake.json", "inspection.json", "findings.json"]) {
1088
+ const sourcePath = path.join(sidecarRoot, fileName);
1089
+ if (!(await fileExists(sourcePath))) {
1090
+ continue;
1091
+ }
1092
+ const targetPath = path.join(snapshotDir, fileName);
1093
+ await copyFile(sourcePath, targetPath);
1094
+ files.push(repoRelativePath(repoRoot, targetPath));
1095
+ }
1096
+ const checkPath = path.join(snapshotDir, "check.json");
1097
+ await writeFile(checkPath, `${JSON.stringify({
1098
+ snapshot_at: snapshotAt,
1099
+ surface: surfaceId,
1100
+ check,
1101
+ }, null, 2)}\n`, "utf8");
1102
+ files.push(repoRelativePath(repoRoot, checkPath));
1103
+ return {
1104
+ directory: repoRelativePath(repoRoot, snapshotDir),
1105
+ files: files.sort(),
1106
+ };
1107
+ }
1108
+ async function workplanPlan(repoRoot, request, args) {
1109
+ const answers = keyValueFlag(args, "answer");
1110
+ delete answers.feature_surface;
1111
+ const result = await planIntegrationTool.call({
1112
+ repoPath: repoRoot,
1113
+ request,
1114
+ surfacePath: flagValue(args, "surface-path"),
1115
+ answers,
1116
+ });
1117
+ return JSON.parse(result.content.map((item) => item.text).join("\n"));
1118
+ }
1119
+ function socialWorkplanSequence(plan) {
1120
+ const workplan = plan.socialWorkplan;
1121
+ if (!workplan || workplan.kind !== "social-multi-surface" || !Array.isArray(workplan.sequence)) {
1122
+ return [];
1123
+ }
1124
+ return workplan.sequence;
1125
+ }
1126
+ async function readWorkplanProgress(repoRoot) {
1127
+ try {
1128
+ return JSON.parse(await readFile(workplanProgressPath(repoRoot), "utf8"));
1129
+ }
1130
+ catch {
1131
+ return null;
1132
+ }
1133
+ }
1134
+ async function writeWorkplanProgress(repoRoot, progress) {
1135
+ const filePath = workplanProgressPath(repoRoot);
1136
+ await mkdir(path.dirname(filePath), { recursive: true });
1137
+ await writeFile(filePath, `${JSON.stringify(progress, null, 2)}\n`, "utf8");
1138
+ }
1139
+ function workplanProgressPath(repoRoot) {
1140
+ return path.join(sidecarPath(repoRoot), "workplan.json");
1141
+ }
1142
+ function workplanSurfaceSnapshotDir(repoRoot, surfaceId) {
1143
+ const safeSurfaceId = surfaceId.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
1144
+ if (!safeSurfaceId) {
1145
+ throw new Error(`Invalid workplan surface id: "${surfaceId}".`);
1146
+ }
1147
+ return path.join(sidecarPath(repoRoot), "workplan-snapshots", safeSurfaceId);
1148
+ }
1149
+ function sidecarPath(repoRoot) {
1150
+ return path.join(repoRoot, "sp-vise");
1151
+ }
1152
+ function repoRelativePath(repoRoot, filePath) {
1153
+ return path.relative(repoRoot, filePath).split(path.sep).join("/");
1154
+ }
1155
+ function sequenceIndex(sequence, surfaceId) {
1156
+ const index = sequence.findIndex((surface) => surface.id === surfaceId);
1157
+ return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
1158
+ }
1159
+ function shellQuoteForCommand(value) {
1160
+ return `'${value.replace(/'/g, "'\\''")}'`;
1161
+ }
919
1162
  function ciCheckResult(result) {
920
1163
  return {
921
1164
  ...result,
@@ -929,7 +1172,7 @@ function ciCheckResult(result) {
929
1172
  };
930
1173
  }
931
1174
  function positionalRepoPath(args) {
932
- const flagsWithValues = new Set(["request", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source"]);
1175
+ const flagsWithValues = new Set(["request", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source", "note"]);
933
1176
  for (let index = 0; index < args.length; index += 1) {
934
1177
  const arg = args[index];
935
1178
  if (!arg) {
@@ -951,7 +1194,7 @@ function positionalRepoPath(args) {
951
1194
  }
952
1195
  function requiredPositionalText(args, message) {
953
1196
  const values = [];
954
- const flagsWithValues = new Set(["request", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source"]);
1197
+ const flagsWithValues = new Set(["request", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source", "note"]);
955
1198
  for (let index = 0; index < args.length; index += 1) {
956
1199
  const arg = args[index];
957
1200
  if (!arg) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amityco/social-plus-vise",
3
- "version": "0.14.22",
3
+ "version": "0.14.24",
4
4
  "description": "Skill-guided deterministic CLI for social.plus SDK integration assistance.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -19,6 +19,33 @@ vise plan . --request "<user intent>"
19
19
  vise init . --request "<user intent>"
20
20
  ```
21
21
 
22
+ For broad social requests, `vise plan` may return `socialWorkplan.kind:
23
+ "social-multi-surface"`. In that case, do not force the whole request into one
24
+ sidecar. Drive the ordered workplan:
25
+
26
+ ```bash
27
+ vise workplan next . --request "<user intent>"
28
+ # Run the focused plan/init commands printed for the next surface.
29
+ vise plan . --request "<user intent>" --answer feature_surface=<surface> ...
30
+ vise init . --request "<user intent>" --answer feature_surface=<surface> ...
31
+ ```
32
+
33
+ Implement one surface at a time. Surface that focused plan's intake questions to
34
+ the user, including opt-in capability questions, then run the normal check loop.
35
+ After `vise check`, `vise sync`, `vise validate`, and `vise run-sensors` pass,
36
+ record the surface:
37
+
38
+ ```bash
39
+ vise workplan complete . --request "<user intent>" --surface <surface>
40
+ vise workplan status . --request "<user intent>"
41
+ ```
42
+
43
+ `workplan complete` is evidence-backed: it refuses non-green `vise check`
44
+ results and snapshots the surface's compliance/check sidecar under
45
+ `sp-vise/workplan-snapshots/<surface>/`. Repeat until `workplan status` reports
46
+ the broad request complete, then run one final `vise check`, `vise validate`,
47
+ and `vise run-sensors` before handoff.
48
+
22
49
  Then fetch docs cited by the plan or relevant to the requested feature:
23
50
 
24
51
  ```bash
@@ -39,9 +66,18 @@ vise run-sensors .
39
66
 
40
67
  **Feed-forward optional capabilities require an explicit answer.** When `vise plan` returns `optionalCapabilities` or the intake question `feed_optional_capabilities`, do not silently ignore it. Either pass the selected exact ids through both `vise plan` and `vise init`, for example `--answer feed_optional_capabilities=post-image-upload,post-poll-creation`, or pass `--answer feed_optional_capabilities=none` and state that the optional capabilities are out of scope. Selected optional capabilities become source sensors in `vise check`; unselected ones remain non-mandatory.
41
68
 
69
+ **Design confirmation must be shown, not assumed.** When `vise plan` or `vise init`
70
+ asks `design_contract_confirmation`, open `sp-vise/design-preview.html` (or run
71
+ `vise design extract --from-project` / `vise design preview` first if no preview
72
+ exists) and show it to the user for approval. Pass
73
+ `--answer design_contract_confirmation=yes` only after approval. If the user says
74
+ no, do not pass `yes`; revise the design source and regenerate the preview, or
75
+ continue only after the user explicitly scopes the work without design
76
+ feed-forward.
77
+
42
78
  **Blocking intake questions must be surfaced to the user.** If `vise plan` returns `intake.status: "needs-clarification"` with blocking questions, stop and ask those questions before implementation. Current Vise also enforces this at `vise init`: unresolved blocking intake returns `status: "needs-clarification"` and the CLI exits non-zero. Only retrospective benchmark/harness setup may pass `--allow-unresolved-intake`, and that acknowledgement is recorded in `sp-vise/intake.json`.
43
79
 
44
- **Baseline completeness is also a stop condition.** When `vise check .` exits with status `completeness-gap` (exit code 5), one or more baseline capabilities are neither implemented nor opted-out. For add-feed, the baseline capability is pagination. For each missing item: either implement it, or place `// vise: scope-omit <id> — <reason>` in the relevant source file and re-run `vise check .` to confirm it exits 0. A missing baseline capability that is neither built nor opted-out is a silent drop — the check will not pass green until every baseline capability is resolved.
80
+ **Baseline completeness is also a stop condition.** When `vise check .` exits with status `completeness-gap` (exit code 5), one or more baseline capabilities are neither implemented nor opted-out. For add-feed, the baseline capability is pagination. For add-comments, the baseline capability is the comment composer/write affordance. For add-chat, the baseline capabilities are message send plus read/unread state. For add-follow/profile, the baseline capability is SDK-backed follower/following relationship data. For each missing item: either implement it, or place `// vise: scope-omit <id> — <reason>` in the relevant source file and re-run `vise check .` to confirm it exits 0. A missing baseline capability that is neither built nor opted-out is a silent drop — the check will not pass green until every baseline capability is resolved.
45
81
 
46
82
  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.
47
83
 
@@ -248,7 +284,7 @@ vise plan . --request "<feed or post request>"
248
284
 
249
285
  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.
250
286
 
251
- **Decide engagement scope explicitly — Vise authors the baseline checklist, and optional feed capabilities require user opt-in.** `vise plan` returns a `completenessChecklist` for baseline capabilities (for add-feed today: pagination) and an `optionalCapabilities` block for feed-forward choices such as image upload, poll creation, and edit post. `vise check` reports baseline capabilities as present / missing / opted-out. Missing baseline items that are neither built nor validly opted-out produce `completeness-gap` (exit code 5), because they are silent drops. For each baseline capability: build it, or explicitly opt out with a recorded marker in the code so the omission is reviewable, not accidental:
287
+ **Decide engagement scope explicitly — Vise authors the baseline checklist, and optional feed capabilities require user opt-in.** `vise plan` returns a `completenessChecklist` for baseline capabilities (for add-feed today: pagination; for add-comments: comment composer/write affordance; for add-chat: send plus read/unread state; for add-follow/profile: follower/following relationship data) and an `optionalCapabilities` block for feed-forward choices such as image upload, poll creation, and edit post. `vise check` reports baseline capabilities as present / missing / opted-out. Missing baseline items that are neither built nor validly opted-out produce `completeness-gap` (exit code 5), because they are silent drops. For each baseline capability: build it, or explicitly opt out with a recorded marker in the code so the omission is reviewable, not accidental:
252
288
 
253
289
  ```
254
290
  // vise: scope-omit pagination — single-screen feed; no load-more affordance in this integration