@amityco/social-plus-vise 0.14.21 → 0.14.23

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.23 — 2026-06-06
8
+
9
+ ### Added
10
+ - **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`.
11
+
12
+ ### Verified
13
+ - CLI regression coverage now locks next-surface selection, completion recording, and feed-to-comments progression for broad Android social workplans.
14
+ - 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.
15
+
16
+ ## 0.14.22 — 2026-06-05
17
+
18
+ ### Added
19
+ - **Multi-surface social workplans:** broad social requests now include a `socialWorkplan` sequence that decomposes feed, comments, chat, and profile work into ordered per-surface plans with focused `vise plan` / `vise init` commands.
20
+
21
+ ### Changed
22
+ - **Broad social intake:** when a multi-surface workplan exists, Vise no longer blocks the top-level plan on `feature_surface`; focused per-surface runs still use `--answer feature_surface=<surface>` before implementation.
23
+
24
+ ### Verified
25
+ - Product-flow, CLI, fixture, improvement-discovery, and host-agent smoke coverage now lock the multi-surface workplan path while preserving answered single-surface init behavior.
26
+
7
27
  ## 0.14.21 — 2026-06-05
8
28
 
9
29
  ### Changed
package/README.md CHANGED
@@ -161,13 +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.21 carries current release proof around the full feed-forward, product-expectation, and validation flow:
164
+ Version 0.14.23 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
- | **Plan questions** | Plans surface blocking questions such as `feature_surface` and `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`. |
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 progress in `sp-vise/workplan.json`. |
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. |
170
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.22` reached `vise check` green with **43/43 deterministic passes**, passed `vise validate`, and passed Gradle assemble/unit-test sensors. This is dogfood evidence, not a controlled multi-agent benchmark. |
171
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. |
172
174
  | **Rule detection** | TP-track dashboard detects **321/321 seeded rule gaps (100.0%)** in the static corpus. |
173
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. |
@@ -298,6 +300,9 @@ The flow above is what the skill teaches your AI agent. You — the human — dr
298
300
  | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors |
299
301
  | `vise plan [path] --request "..."` | Produce a grounded implementation plan with intake questions and docs citations |
300
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 in `sp-vise/workplan.json` after `check`, `sync`, `validate`, and sensors pass |
301
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 |
302
307
  | `vise blocks list --registry <path>` | Read a social.plus Block Factory registry |
303
308
  | `vise blocks plan [path] --block <id> --registry <path>` | Plan safe block package, source-anchor, sidecar, and sensor changes |
@@ -453,6 +458,7 @@ After a successful `vise init`, your project gets a `sp-vise/` directory. If ini
453
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. |
454
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). |
455
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, and optional host-agent notes. |
456
462
  | `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. |
457
463
  | `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`. |
458
464
  | `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. |
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,148 @@ 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 existing = await readWorkplanProgress(repoRoot);
1019
+ const base = existing?.request === request
1020
+ ? existing
1021
+ : {
1022
+ schema_version: 1,
1023
+ vise_version: packageVersion,
1024
+ request,
1025
+ generated_at: now,
1026
+ updated_at: now,
1027
+ completed: [],
1028
+ };
1029
+ const withoutSurface = base.completed.filter((item) => item.surface !== surfaceId);
1030
+ const progress = {
1031
+ ...base,
1032
+ vise_version: packageVersion,
1033
+ updated_at: now,
1034
+ completed: [
1035
+ ...withoutSurface,
1036
+ {
1037
+ surface: surfaceId,
1038
+ outcome: surface.outcome,
1039
+ completed_at: now,
1040
+ ...(note ? { note } : {}),
1041
+ },
1042
+ ].sort((a, b) => sequenceIndex(sequence, a.surface) - sequenceIndex(sequence, b.surface)),
1043
+ };
1044
+ await writeWorkplanProgress(repoRoot, progress);
1045
+ const status = await workplanStatus(args);
1046
+ return {
1047
+ status: "recorded",
1048
+ progressPath: workplanProgressPath(repoRoot),
1049
+ recorded: progress.completed.find((item) => item.surface === surfaceId),
1050
+ nextSurface: status.nextSurface,
1051
+ nextStep: status.nextStep,
1052
+ };
1053
+ }
1054
+ async function workplanPlan(repoRoot, request, args) {
1055
+ const answers = keyValueFlag(args, "answer");
1056
+ delete answers.feature_surface;
1057
+ const result = await planIntegrationTool.call({
1058
+ repoPath: repoRoot,
1059
+ request,
1060
+ surfacePath: flagValue(args, "surface-path"),
1061
+ answers,
1062
+ });
1063
+ return JSON.parse(result.content.map((item) => item.text).join("\n"));
1064
+ }
1065
+ function socialWorkplanSequence(plan) {
1066
+ const workplan = plan.socialWorkplan;
1067
+ if (!workplan || workplan.kind !== "social-multi-surface" || !Array.isArray(workplan.sequence)) {
1068
+ return [];
1069
+ }
1070
+ return workplan.sequence;
1071
+ }
1072
+ async function readWorkplanProgress(repoRoot) {
1073
+ try {
1074
+ return JSON.parse(await readFile(workplanProgressPath(repoRoot), "utf8"));
1075
+ }
1076
+ catch {
1077
+ return null;
1078
+ }
1079
+ }
1080
+ async function writeWorkplanProgress(repoRoot, progress) {
1081
+ const filePath = workplanProgressPath(repoRoot);
1082
+ await mkdir(path.dirname(filePath), { recursive: true });
1083
+ await writeFile(filePath, `${JSON.stringify(progress, null, 2)}\n`, "utf8");
1084
+ }
1085
+ function workplanProgressPath(repoRoot) {
1086
+ return path.join(repoRoot, "sp-vise", "workplan.json");
1087
+ }
1088
+ function sequenceIndex(sequence, surfaceId) {
1089
+ const index = sequence.findIndex((surface) => surface.id === surfaceId);
1090
+ return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
1091
+ }
1092
+ function shellQuoteForCommand(value) {
1093
+ return `'${value.replace(/'/g, "'\\''")}'`;
1094
+ }
919
1095
  function ciCheckResult(result) {
920
1096
  return {
921
1097
  ...result,
@@ -929,7 +1105,7 @@ function ciCheckResult(result) {
929
1105
  };
930
1106
  }
931
1107
  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"]);
1108
+ 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
1109
  for (let index = 0; index < args.length; index += 1) {
934
1110
  const arg = args[index];
935
1111
  if (!arg) {
@@ -951,7 +1127,7 @@ function positionalRepoPath(args) {
951
1127
  }
952
1128
  function requiredPositionalText(args, message) {
953
1129
  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"]);
1130
+ 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
1131
  for (let index = 0; index < args.length; index += 1) {
956
1132
  const arg = args[index];
957
1133
  if (!arg) {
@@ -82,7 +82,21 @@ async function buildIntegrationPlan(repoPath, request, surfacePath, answers = {}
82
82
  const designReview = designReviewGuidance(repoRoot, designContract, answers);
83
83
  const acceptedDesignContract = designReview.status === "accepted" ? designContract : null;
84
84
  const designBrief = acceptedDesignContract ? buildDesignBrief(acceptedDesignContract) : undefined;
85
- const intake = intakeFor(ctx, definition.intakeQuestions(ctx), outcome, designBrief, capabilityAvailability, designReview);
85
+ const socialWorkplan = await socialWorkplanFor({
86
+ request,
87
+ answers,
88
+ repoPath: repoRoot,
89
+ platform,
90
+ platforms: inspection.platforms,
91
+ designSignals: inspection.designSignals,
92
+ designBrief,
93
+ designReview,
94
+ sensors,
95
+ });
96
+ const outcomeQuestions = socialWorkplan
97
+ ? definition.intakeQuestions(ctx).filter((question) => question.id !== "feature_surface")
98
+ : definition.intakeQuestions(ctx);
99
+ const intake = intakeFor(ctx, outcomeQuestions, outcome, designBrief, capabilityAvailability, designReview);
86
100
  // Advisory SDK-version currency guidance (npm registry for TS/RN; version-agnostic
87
101
  // for native). Best-effort — degrades to greenfield "install latest + pin" if the
88
102
  // registry is unreachable. Never gates.
@@ -108,6 +122,7 @@ async function buildIntegrationPlan(repoPath, request, surfacePath, answers = {}
108
122
  platform,
109
123
  supportLevel,
110
124
  intent: intentFor(request, definition.interpretation),
125
+ socialWorkplan,
111
126
  capabilityAvailability,
112
127
  designReview,
113
128
  decisionsRequired,
@@ -123,7 +138,7 @@ async function buildIntegrationPlan(repoPath, request, surfacePath, answers = {}
123
138
  availableSurfaces: inspection.surfaces,
124
139
  applicableRules: await applicableCompliancePlanRuleSummaries(outcome, inspection.platforms),
125
140
  sensors: sensors.map((sensor) => ({ name: sensor.name, command: sensor.command, source: sensor.source })),
126
- stopConditions: composeStopConditions(ctx, definition.stopConditions(ctx), inspection.surfaces, surfacePath),
141
+ stopConditions: composeStopConditions(ctx, definition.stopConditions(ctx), inspection.surfaces, surfacePath, Boolean(socialWorkplan)),
127
142
  evidencePolicy: "Every implementation step must cite at least one detected file, docs page, validator rule, or required user input. If evidence is missing, stop and ask the user instead of inventing details.",
128
143
  designContract: acceptedDesignContract ? designContractGuidance(acceptedDesignContract) : undefined,
129
144
  completenessChecklist: completenessChecklistFor(outcome),
@@ -162,6 +177,113 @@ function optionalCapabilitiesFor(outcome, answers, request, availability) {
162
177
  selected: selectedOptionalCapabilityIds(outcome, answers, request, availableIds),
163
178
  };
164
179
  }
180
+ const SOCIAL_SURFACE_SEQUENCE = [
181
+ {
182
+ id: "feed",
183
+ outcome: "add-feed",
184
+ label: "Feed and post creation",
185
+ aliases: [/\b(feed|timeline|news feed|post list|posts?|create post|post creation|compose post|composer|reactions?)\b/i],
186
+ defaultForGenericBroad: true,
187
+ },
188
+ {
189
+ id: "comments",
190
+ outcome: "add-comments",
191
+ label: "Comments and replies",
192
+ aliases: [/\b(comments?|replies|reply|discussion thread|comment tray|comment composer)\b/i],
193
+ defaultForGenericBroad: true,
194
+ },
195
+ {
196
+ id: "chat",
197
+ outcome: "add-chat",
198
+ label: "Chat inbox and thread",
199
+ aliases: [/\b(chat|messaging|dm|direct message|conversation|group chat|channel|inbox)\b/i],
200
+ defaultForGenericBroad: true,
201
+ },
202
+ {
203
+ id: "profile",
204
+ outcome: "add-follow",
205
+ label: "Profile and follow graph",
206
+ aliases: [/\b(profile|profiles|follow|unfollow|followers?|following|social graph|relationship)\b/i],
207
+ defaultForGenericBroad: true,
208
+ },
209
+ {
210
+ id: "community",
211
+ outcome: "add-community",
212
+ label: "Community management",
213
+ 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],
214
+ },
215
+ {
216
+ id: "notifications",
217
+ outcome: "add-notifications",
218
+ label: "In-app notifications",
219
+ aliases: [/\b(notification tray|notification cent(?:er|re)|in-?app notifications?|notification settings?|notification preferences?)\b/i],
220
+ },
221
+ ];
222
+ async function socialWorkplanFor(args) {
223
+ if (!BROAD_SOCIAL_REGEX.test(args.request) || hasAnswer(args.answers, "feature_surface")) {
224
+ return undefined;
225
+ }
226
+ const matched = SOCIAL_SURFACE_SEQUENCE.filter((surface) => surface.aliases.some((pattern) => pattern.test(args.request)));
227
+ const selected = matched.length > 0
228
+ ? matched
229
+ : SOCIAL_SURFACE_SEQUENCE.filter((surface) => surface.defaultForGenericBroad);
230
+ if (selected.length <= 1) {
231
+ return undefined;
232
+ }
233
+ const sequence = [];
234
+ for (const surface of selected) {
235
+ const definition = getOutcomeDefinition(surface.outcome);
236
+ const capabilityAvailability = await platformCapabilityAvailability(surface.outcome, args.platform);
237
+ const surfaceAnswers = { ...args.answers, feature_surface: surface.id };
238
+ const surfaceCtx = {
239
+ ...planContextFor({
240
+ request: args.request,
241
+ outcome: surface.outcome,
242
+ platform: args.platform,
243
+ platforms: args.platforms,
244
+ designSignals: args.designSignals,
245
+ answers: surfaceAnswers,
246
+ }),
247
+ broadSocialRequest: false,
248
+ };
249
+ const intake = intakeFor(surfaceCtx, definition.intakeQuestions(surfaceCtx), surface.outcome, args.designBrief, capabilityAvailability, args.designReview);
250
+ const matchedPrompt = matched.some((match) => match.id === surface.id);
251
+ const commonArgs = `. --request ${shellQuote(args.request)} --answer feature_surface=${surface.id}`;
252
+ sequence.push({
253
+ id: surface.id,
254
+ order: sequence.length + 1,
255
+ outcome: surface.outcome,
256
+ label: surface.label,
257
+ matchedPrompt,
258
+ reason: matchedPrompt
259
+ ? `The request explicitly mentions ${surface.label.toLowerCase()}.`
260
+ : "The request is generic broad social work, so Vise includes this core surface in the default sequence.",
261
+ planCommand: `vise plan ${commonArgs}`,
262
+ initCommand: `vise init ${commonArgs}`,
263
+ intake: {
264
+ status: intake.status,
265
+ questions: intake.questions,
266
+ remainingBlocking: intake.remainingBlocking,
267
+ },
268
+ capabilityAvailability,
269
+ validation: ["validate_setup", "run_sensors", ...definition.validation(args.platform)],
270
+ docs: definition.docs(args.platform).filter((doc) => doc.path !== "unknown"),
271
+ optionalCapabilities: optionalCapabilitiesFor(surface.outcome, surfaceAnswers, args.request, capabilityAvailability),
272
+ sensors: args.sensors.map((sensor) => ({ name: sensor.name, command: sensor.command, source: sensor.source })),
273
+ });
274
+ }
275
+ const status = sequence.some((surface) => surface.intake.remainingBlocking > 0) ? "needs-answers" : "ready";
276
+ return {
277
+ kind: "social-multi-surface",
278
+ status,
279
+ note: "This broad social request is decomposed into ordered per-surface plans. Initialize and check one surface at a time so each sidecar has one outcome, while using this sequence as the coordinated product workplan.",
280
+ sequence,
281
+ nextStep: "Resolve each surface's blocking questions, then run the listed plan/init command for that surface before implementation. After each surface build, run `vise check .`, `vise sync .`, `vise validate .`, and `vise run-sensors .`.",
282
+ };
283
+ }
284
+ function shellQuote(value) {
285
+ return `'${value.replace(/'/g, "'\\''")}'`;
286
+ }
165
287
  function designReviewGuidance(repoRoot, contract, answers) {
166
288
  if (!contract) {
167
289
  return {
@@ -371,7 +493,7 @@ function composeImplementationRules(ctx, outcomeRules) {
371
493
  }
372
494
  return rules;
373
495
  }
374
- function composeStopConditions(ctx, outcomeStops, surfaces, surfacePath) {
496
+ function composeStopConditions(ctx, outcomeStops, surfaces, surfacePath, hasSocialWorkplan = false) {
375
497
  const stops = [
376
498
  "A required secret is missing and no safe ignored local env file or non-secret template path is clear.",
377
499
  "The target file is ambiguous or missing and no safe conventional location is detected.",
@@ -387,7 +509,7 @@ function composeStopConditions(ctx, outcomeStops, surfaces, surfacePath) {
387
509
  stops.push(`Multiple app surfaces detected (${surfaces.map((surface) => surface.path).join(", ")}); call this tool again with surfacePath set to the target app surface.`);
388
510
  }
389
511
  stops.push(...outcomeStops);
390
- if (ctx.broadSocialRequest && !hasAnswer(ctx.answers, "feature_surface")) {
512
+ if (ctx.broadSocialRequest && !hasAnswer(ctx.answers, "feature_surface") && !hasSocialWorkplan) {
391
513
  stops.push("The requested social feature is too broad; confirm the first feature surface before implementing.");
392
514
  }
393
515
  if (ctx.mentionsDesign && ctx.designSignals.length === 0 && !hasAnswer(ctx.answers, "design_source")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amityco/social-plus-vise",
3
- "version": "0.14.21",
3
+ "version": "0.14.23",
4
4
  "description": "Skill-guided deterministic CLI for social.plus SDK integration assistance.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",