@amityco/social-plus-vise 0.14.26 → 0.14.27

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,21 @@ 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.27 — 2026-06-10
8
+
9
+ **Theme:** social-plus-forge monorepo move. No runtime behavior changes.
10
+
11
+ ### Changed
12
+ - **Repository metadata:** the package now lives in the `vise/` module of the [social-plus-forge monorepo](https://github.com/AmityCo/social-plus-forge); `repository` (with `directory: vise`), `homepage`, and `bugs` URLs updated from the retired `social-plus-foundry`/`social-plus-vise` repos.
13
+ - **Tarball slimmed 1.9 MB → ~0.5 MB:** `social.plus-vise.png` is no longer shipped in the npm package (it was 79% of the install and never referenced by the README).
14
+
15
+ ### Fixed
16
+ - **`ajv` declared as a devDependency:** it was imported by the catalog-schema gate and `scripts/catalog-sheets.mjs` but rode in transitively under npm's flat hoisting; pnpm's strict layout surfaced the gap.
17
+
18
+ ### Docs
19
+ - Backfilled the `vise blocks` installer namespace into the `0.14.5` changelog entry (shipped there, never recorded).
20
+ - `RELEASE.md` no longer hardcodes the current npm version; `RULES.md`/`ARCHITECTURE.md` defer exact rule counts to the rule-coverage gate.
21
+
7
22
  ## 0.14.26 — 2026-06-06
8
23
 
9
24
  ### Added
@@ -217,6 +232,9 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/
217
232
 
218
233
  **Theme:** Optional feed capabilities become explicit opt-in sensors.
219
234
 
235
+ ### Added
236
+ - *(backfilled 2026-06-10 — shipped in this release but was never recorded)* **Block installer namespace:** `vise blocks list / plan / add / validate` (PR #143). Reads a social.plus Block Factory registry, plans safe package/source-anchor/sidecar changes, applies them behind `--dry-run`/`--apply`, writes `sp-vise/blocks.json`, and validates installed block state and drift.
237
+
220
238
  ### Changed
221
239
  - **Add-feed completeness baseline narrowed to pagination:** image upload, poll creation, and edit post are no longer baseline completeness requirements. The add-feed `completenessChecklist` now gates pagination / load-more only.
222
240
  - **Optional feed capabilities require explicit opt-in:** `vise plan` now offers image upload, poll creation, and edit post as `optionalCapabilities`. When the user selects one and the agent carries it into `vise init --answer feed_optional_capabilities=...`, `vise check` runs selected source sensors and exits `selected-capability-failures` (exit code 6) until the selected capability is implemented.
package/README.md CHANGED
@@ -85,6 +85,8 @@ Correctness is gated by deterministic rules or attestations. Baseline completene
85
85
 
86
86
  Vise's next architecture track is the social.plus **Engagement Intelligence System**: an outcome-to-experience layer that maps customer goals, archetypes, solution patterns, experience objects, UX patterns, and variants before normal implementation planning. The first advisory runtime slice is `vise creative`: it consumes a request plus optional requirements/prototype inputs, or runs in exploratory mode when requirements are absent, writes a local creative brief for user variant selection, and records the accepted variant so `vise plan`, `vise init`, and `vise workplan` can carry that product/UX direction forward. See [docs/ENGAGEMENT_INTELLIGENCE_SYSTEM.md](docs/ENGAGEMENT_INTELLIGENCE_SYSTEM.md), [docs/MONOREPO_ARCHITECTURE.md](docs/MONOREPO_ARCHITECTURE.md), and [packages/intelligence](packages/intelligence).
87
87
 
88
+ The current milestone is the **Learning Engine sensor bridge with score calibration guardrails**. After creative selection, experience compilation, and sensor review, Vise can keep local learning events that snapshot the selected variant, outcome, Experience Report, and Experience Sensor Framework, then summarize recurring review gaps by variant, outcome, and dimension. The shadow calibration track now has 70 measured cells, including a v2 independent candidate-ranking matrix that ranked the expected variant first in 21/21 registered cells while preserving visible review gaps. The v2 human gate has named product FP/FN and privacy sign-off recorded, including approval that `event-first` beats `creator-first` in the five thin-margin cross-platform cells. Gate 2 exposes this evidence only as an explicit `vise creative --ranking-preview` local advisory preview; the current local dogfood pass covers 18 synthetic prompts spanning ten archetypes and all ten catalog variants (seven available, three gamification variants gated as in-development). Of the 14 available-variant cases, 13 align and 1 is a declared near-miss — in the opt-in preview only, the shadow-policy-v2-draft ranking ranks `live-commerce` just above `discovery-first` for an ecommerce social-discovery prompt, while the brief's keyword default and the agent both pick the human-correct `discovery-first` — with 0 undeclared ranking concerns and 0 candidate-surfacing gaps; the other 4 gamification cases are correctly gated. The paired `live-shopping-stream` control (a clear live-selling prompt) shows the preview correctly elevating `live-commerce` over the keyword default. There are still no uploads, no changed `vise check` exit codes, no calibrated single score, no auto-acceptance, and no default recommendation ranking. See [docs/UX_HARNESS_MVP.md](docs/UX_HARNESS_MVP.md), [docs/LEARNING_ENGINE_MVP.md](docs/LEARNING_ENGINE_MVP.md), [docs/EXPERIENCE_SCORE_CALIBRATION.md](docs/EXPERIENCE_SCORE_CALIBRATION.md), and [docs/ENGAGEMENT_INTELLIGENCE_SYSTEM.md](docs/ENGAGEMENT_INTELLIGENCE_SYSTEM.md).
89
+
88
90
  ### Relationship to social.plus Block Factory
89
91
 
90
92
  Vise has two deliberately separate roles:
@@ -170,11 +172,12 @@ Version 0.14.26 carries current release proof around the full feed-forward, prod
170
172
  | Surface | What was validated |
171
173
  |---|---|
172
174
  | **Creative pre-planning** | `vise creative` produces requirements-driven or exploratory Engagement Intelligence briefs, writes `sp-vise/creative-brief.json` / `.md`, asks for `preferred_solution`, and survives packed-package installation with the intelligence catalog bundled. |
173
- | **Creative selection bridge** | `vise creative accept --variant <id>` writes `sp-vise/creative-selection.json`; matching `vise plan`, `vise init`, and `vise workplan next` runs surface the accepted variant, derive workplan surfaces from its experience objects, and mirror the selection into `sp-vise/intake.json`. |
175
+ | **Creative selection bridge** | `vise creative accept --variant <id>` writes `sp-vise/creative-selection.json` and `sp-vise/ux-harness.json`; matching `vise plan`, `vise init`, and `vise workplan next` runs surface the accepted variant, UX expectations, derive workplan surfaces from its experience objects, and mirror the selection into `sp-vise/intake.json`. |
174
176
  | **Product flow** | Local end-to-end smoke covers design extraction, plan feed-forward, blocking intake, answered init, capability check, design conformance, and sensor discovery. |
175
177
  | **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>/`. |
176
178
  | **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. |
177
179
  | **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`. |
180
+ | **Experience Score calibration guard** | Experience Report, Experience Sensors, and Learning Engine snapshots keep `score: null`, while `learning-summary.json` keeps `recommendationOptimization.status: "not-active"`. The shadow benchmark has 70 measured cells; `shadow-policy-v2-draft` independently ranked expected variants first in 21/21 registered ranking/cross-platform cells, its human gate package has named product FP/FN and privacy sign-off recorded, and runtime exposure is limited to the opt-in local `vise creative --ranking-preview` artifact. |
178
181
  | **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. |
179
182
  | **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. |
180
183
  | **Rule detection** | TP-track dashboard detects **321/321 seeded rule gaps (100.0%)** in the static corpus. |
@@ -187,6 +190,7 @@ Version 0.14.26 carries current release proof around the full feed-forward, prod
187
190
  | **Feature completeness** | Vise helps agents build more of the expected SDK capability surface. | Latest comparison: **21-30% without Vise vs 97-100% with Vise**, with **98/99** expected feed capabilities implemented in aggregate. Earlier Capability Matrix work also showed silently dropped items falling from 7.67/11 to 4.0/11. |
188
191
  | **SDK compliance** | Vise checks catch greenfield SDK compliance gaps that docs or static guidance can miss. | Commune benchmark: Vise averaged **100% greenfield SDK compliance** where docs/RAG-style controls averaged **67%** across the reported slices. |
189
192
  | **Design conformance** | Vise design checks reduce design drift under ambiguous briefs. | Ambiguous Spotify-style design test: Vise design runs produced **0 / 0 / 0 hex literals** across three seeds; without Vise, runs varied **0 / 2 / 15**. This supports variance reduction, not pixel-perfect visual quality. |
193
+ | **Candidate ranking calibration** | Vise can optionally preview `shadow-policy-v2-draft` ranking for surfaced creative variants, without changing default runtime recommendations. | Experience calibration track: **70 measured cells**. `shadow-policy-v2-draft` ranked the expected variant first in **21/21** cells, produced **14** improvement opportunities, has named product approval for **5** thin-margin event-first-over-creator-first cells plus named privacy approval for the benchmark evidence boundary, and is exposed only through explicit `--ranking-preview`. Current local dogfood has **18** prompts (ten archetypes, all **ten** variants — seven available, three gamification gated): of the **14** available-variant cases, **13** aligned and **1** declared near-miss (in the opt-in preview only; the keyword default and the agent both pick the human-correct variant), **0** undeclared ranking concerns, **0** candidate-surfacing gaps; the other **4** gamification cases are gated, and the paired positive control shows the preview correctly elevating the human-correct variant over the keyword default; default activation remains disabled. |
190
194
 
191
195
  ### Why the workflow works
192
196
 
@@ -304,8 +308,14 @@ The flow above is what the skill teaches your AI agent. You — the human — dr
304
308
  |---|---|
305
309
  | `vise doctor` | Verify install; print version, install path, docs source |
306
310
  | `vise inspect [path]` | Detect platform, monorepo surfaces, design signals, available sensors |
307
- | `vise creative [path] --request "..." [--requirements <path\|none>] [--prototype <html>]` | Produce an advisory Engagement Intelligence brief with 2-3 solution variants before normal implementation planning; writes `sp-vise/creative-brief.json` and `.md` unless `--no-write` is set |
308
- | `vise creative accept [path] --variant <id>` | Accept a creative variant and write `sp-vise/creative-selection.json` so subsequent `plan`, `init`, and `workplan` runs include the selected product/UX direction |
311
+ | `vise creative [path] --request "..." [--requirements <path\|none>] [--prototype <html>] [--ranking-preview]` | Produce an advisory Engagement Intelligence brief with 2-3 solution variants before normal implementation planning; `--ranking-preview` also writes an opt-in local candidate-ranking preview without changing the candidate order |
312
+ | `vise creative accept [path] --variant <id>` | Accept a creative variant and write `sp-vise/creative-selection.json` so subsequent `plan`, `init`, and `workplan` runs include the selected product/UX direction. Use `--variant none --rationale "<what is missing>"` to instead record a local `sp-vise/catalog-gap.json` no-fit signal for human catalog review |
313
+ | `vise ux-harness [path]` | Generate or refresh `sp-vise/ux-harness.json` from the accepted creative selection; advisory UX expectations only |
314
+ | `vise experience compile [path]` | Compile the accepted creative variant into `sp-vise/experience-compiler.json`: install guidance, navigation placement, surface plans, UX expectations, Block Factory bridge candidates, design adaptation, and validation commands |
315
+ | `vise experience sensors [path]` | Write `sp-vise/experience-sensors.json`, an advisory sensor framework across technical, design, UX, accessibility, and business-alignment dimensions; no calibrated score yet |
316
+ | `vise experience-report [path]` | Write `sp-vise/experience-report.json`, an advisory dimensioned report for technical compliance, design, UX Harness evidence, and business alignment; no calibrated single score yet |
317
+ | `vise learning record [path]` | Append a local-only learning event for explicit feedback, outcome metrics, or report review; refreshes `sp-vise/learning-summary.json` |
318
+ | `vise learning show [path]` | Read the local learning summary; recommendation ranking is not changed by these events yet |
309
319
  | `vise plan [path] --request "..."` | Produce a grounded implementation plan with intake questions and docs citations |
310
320
  | `vise plan-harness [path] --request "..."` | (Pre-planning step) Build the harness around the request |
311
321
  | `vise workplan next [path] --request "..."` | For broad social requests, print the next uncompleted surface plus focused `plan` / `init` / verification commands |
@@ -410,7 +420,7 @@ MCP-capable hosts can call Vise as structured tool calls instead of shell comman
410
420
 
411
421
  ### Tool names (snake_case per MCP convention)
412
422
 
413
- `inspect_project`, `creative_brief`, `creative_accept`, `plan_harness`, `plan_integration`, `init_compliance`, `check_compliance`, `sync_compliance`, `attest_rule`, `explain_rule`, `init_engagement`, `show_engagement`, `resolve_request`, `search_docs`, `get_doc_page`, `debug_issue`, `validate_setup`, `run_sensors`, `suggest_patch`, `design_extract`, `design_check`, `design_preview`, `design_reference`, `design_init_tokens`.
423
+ `inspect_project`, `creative_brief`, `creative_accept`, `ux_harness`, `compile_experience`, `experience_sensors`, `plan_harness`, `plan_integration`, `init_compliance`, `check_compliance`, `experience_report`, `record_learning`, `show_learning`, `sync_compliance`, `attest_rule`, `explain_rule`, `init_engagement`, `show_engagement`, `resolve_request`, `search_docs`, `get_doc_page`, `debug_issue`, `validate_setup`, `run_sensors`, `suggest_patch`, `design_extract`, `design_check`, `design_preview`, `design_reference`, `design_init_tokens`.
414
424
 
415
425
  These are the same operations as the CLI commands above, exposed as MCP tools.
416
426
 
@@ -466,7 +476,15 @@ Vise writes local planning, compliance, design, and evidence artifacts under `sp
466
476
  | `sp-vise/intake.json` | `vise init` | The request, outcome, intake answers, remaining blocking count, optional creative-selection mirror, design-review status (`absent`, `needs-confirmation`, `accepted`, or `rejected`), and any retrospective `--allow-unresolved-intake` acknowledgement. |
467
477
  | `sp-vise/creative-brief.json` | `vise creative` | Advisory Engagement Intelligence brief: mode, objective, inferred goals/archetypes, candidate solution variants, feasibility summary, preferred-solution question, and next plan/workplan commands. |
468
478
  | `sp-vise/creative-brief.md` | `vise creative` | Human-readable version of the creative brief for review with the user before selecting a variant. |
479
+ | `sp-vise/candidate-ranking-preview.json` | `vise creative --ranking-preview` | Opt-in local advisory ranking preview for the surfaced creative candidates using `shadow-policy-v2-draft`; preserves the original candidate order and writes `experience_score: null` with runtime boundary fields confirming no uploads, no auto-acceptance, and no default ranking change. |
469
480
  | `sp-vise/creative-selection.json` | `vise creative accept` | Accepted creative variant: source brief, selected goals/archetypes/patterns/objects/UX patterns, surface hints, feasibility notes, and plan/workplan feed-forward context. |
481
+ | `sp-vise/catalog-gap.json` | `vise creative accept --variant none` | Recorded no-fit: the request the agent found no fitting variant for, what the catalog is missing, the nearest existing variant, and the grounding signal. A local-only signal for human catalog review — it does not accept a variant, change the catalog, or upload anything. |
482
+ | `sp-vise/ux-harness.json` | `vise creative accept` or `vise ux-harness` | Advisory UX Harness: selected UX pattern expectations, tradeoffs, anti-patterns, surface expectations, and capability-feed-forward boundary notes. |
483
+ | `sp-vise/experience-compiler.json` | `vise experience compile` | Advisory implementation artifact plan: install guidance, navigation placement, focused surface plans, UX expectations, optional Block Factory bridge candidates, design adaptation, and validation commands. |
484
+ | `sp-vise/experience-sensors.json` | `vise experience sensors` | Advisory sensor framework: technical, design, UX, accessibility, and business-alignment evidence summary, review gaps, commands, and `score: null` until calibrated. |
485
+ | `sp-vise/experience-report.json` | `vise experience-report` | Advisory dimensioned review artifact: technical compliance status, design contract presence, UX Harness evidence, business alignment, optional Experience Sensor snapshot, and `score: null` until calibrated by benchmark/dogfood evidence. |
486
+ | `sp-vise/learning-events.jsonl` | `vise learning record` | Append-only local learning event log for explicit customer/reviewer feedback, outcome metrics, selected variant snapshots, outcome snapshots, Experience Report snapshots, and Experience Sensor snapshots. |
487
+ | `sp-vise/learning-summary.json` | `vise learning record` | Derived local learning summary: feedback counts, variant feedback, metric keys, report/sensor snapshots, recurring review gaps by variant/outcome/dimension, and `recommendationOptimization.status: "not-active"`. |
470
488
  | `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). |
471
489
  | `sp-vise/inspection.json` | `vise init` | The platform, monorepo surface, and design-token signals detected at init time. |
472
490
  | `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. |
@@ -1168,7 +1168,7 @@ function symbolHaystack(symbols) {
1168
1168
  return [...new Set(values)];
1169
1169
  }
1170
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).";
1171
- const BASELINE_CAPABILITY_IDS_BY_OUTCOME = {
1171
+ export const BASELINE_CAPABILITY_IDS_BY_OUTCOME = {
1172
1172
  "add-feed": ["pagination"],
1173
1173
  "add-comments": ["comment-composer"],
1174
1174
  "add-chat": ["send-message", "read-state"],
@@ -0,0 +1,107 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ // Deterministic grounding contract for agent-driven variant selection (experience-factory rebuild,
5
+ // Option A). The factory does NOT call a model: the driving coding agent performs the semantic
6
+ // understanding (customer request -> variant) and hands the factory a selection. This module is the
7
+ // factory's deterministic half — it enforces that the agent's selection is GROUNDED in the catalog
8
+ // and surfaces honest review signals. It performs no inference and no IO in its core function, so it
9
+ // is fully reproducible (the determinism lives here; the non-determinism lives in the visible driver).
10
+ //
11
+ // Contract, by evidence from the spike + stress test (benchmarks/experience-calibration/
12
+ // semantic-understanding-spike.mjs and -stresstest.mjs):
13
+ // - variantId MUST be a catalog id, or the literal "none" (the agent's honest no-fit answer).
14
+ // Anything else is a hallucination and is REJECTED.
15
+ // - rationale is REQUIRED (explainability is a core principle of the factory).
16
+ // - confidence is expected (high|medium|low); low confidence and "none" are not failures — they are
17
+ // honest signals that the human/agent should reconsider or that the catalog may need a new variant.
18
+ export const GROUNDING_SCHEMA_VERSION = "2026-06-07.vise-grounding.v1";
19
+ export const NO_FIT = "none";
20
+ const CONFIDENCE_VALUES = ["high", "medium", "low"];
21
+ /**
22
+ * Validate an agent-provided variant selection against the catalog. Pure and deterministic: given the
23
+ * same selection and the same catalog id set it always returns the same result.
24
+ */
25
+ export function groundSelection(selection, catalogVariantIds) {
26
+ const ids = [...catalogVariantIds];
27
+ const idSet = new Set(ids);
28
+ const signals = [];
29
+ const rawVariant = typeof selection?.variantId === "string" ? selection.variantId.trim() : "";
30
+ const rationale = typeof selection?.rationale === "string" ? selection.rationale.trim() : "";
31
+ const rawConfidence = typeof selection?.confidence === "string" ? selection.confidence.trim().toLowerCase() : "";
32
+ const isNoFit = rawVariant.toLowerCase() === NO_FIT;
33
+ const isKnownVariant = idSet.has(rawVariant);
34
+ // 1. Grounding: variant must be a catalog id or the explicit no-fit answer.
35
+ if (!isNoFit && !isKnownVariant) {
36
+ signals.push({
37
+ code: "hallucinated-variant",
38
+ severity: "error",
39
+ message: rawVariant
40
+ ? `Selected variant "${rawVariant}" is not in the catalog. The agent must choose a catalog id or "${NO_FIT}".`
41
+ : `No variant was provided. The agent must choose a catalog id or "${NO_FIT}".`,
42
+ });
43
+ }
44
+ else if (isNoFit) {
45
+ signals.push({
46
+ code: "no-fit",
47
+ severity: "warn",
48
+ message: `The agent reported no catalog variant fits this request. Reconsider the request, or treat this as a candidate for a new catalog variant.`,
49
+ });
50
+ }
51
+ // 2. Rationale is required for explainability (applies to a real variant and to "none").
52
+ if (!rationale) {
53
+ signals.push({
54
+ code: "missing-rationale",
55
+ severity: "error",
56
+ message: `A rationale is required: the selection must explain, grounded in the request and catalog, why this variant (or "${NO_FIT}") was chosen.`,
57
+ });
58
+ }
59
+ // 3. Confidence (advisory but expected). Invalid value is a contract violation; absence/low are signals.
60
+ let confidence = null;
61
+ if (!rawConfidence) {
62
+ signals.push({ code: "missing-confidence", severity: "warn", message: `No confidence was provided; expected one of ${CONFIDENCE_VALUES.join(", ")}.` });
63
+ }
64
+ else if (CONFIDENCE_VALUES.includes(rawConfidence)) {
65
+ confidence = rawConfidence;
66
+ if (confidence === "low" && isKnownVariant) {
67
+ signals.push({ code: "low-confidence", severity: "warn", message: `The agent was not confident in this selection; review before accepting.` });
68
+ }
69
+ }
70
+ else {
71
+ signals.push({ code: "invalid-confidence", severity: "error", message: `Confidence "${rawConfidence}" is invalid; expected one of ${CONFIDENCE_VALUES.join(", ")}.` });
72
+ }
73
+ const hasError = signals.some((s) => s.severity === "error");
74
+ const status = hasError ? "rejected" : signals.length > 0 ? "needs-review" : "grounded";
75
+ const variantId = hasError || isNoFit ? null : rawVariant;
76
+ return {
77
+ schemaVersion: GROUNDING_SCHEMA_VERSION,
78
+ status,
79
+ variantId,
80
+ confidence,
81
+ rationale: rationale || null,
82
+ signals,
83
+ catalogVariantIds: ids,
84
+ reason: summarize(status, variantId, signals),
85
+ };
86
+ }
87
+ function summarize(status, variantId, signals) {
88
+ if (status === "grounded")
89
+ return `Selection "${variantId}" is grounded in the catalog with a rationale.`;
90
+ if (status === "rejected")
91
+ return `Selection rejected: ${signals.filter((s) => s.severity === "error").map((s) => s.code).join(", ")}.`;
92
+ return `Selection accepted with review signals: ${signals.map((s) => s.code).join(", ")}.`;
93
+ }
94
+ /** Resolve the catalog directory shipped in the package (packages/intelligence/catalog). */
95
+ export function catalogDir() {
96
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
97
+ return path.resolve(moduleDir, "..", "..", "packages", "intelligence", "catalog");
98
+ }
99
+ /** Load the set of valid variant ids from the declarative catalog. */
100
+ export async function loadCatalogVariantIds(dir = catalogDir()) {
101
+ const raw = await readFile(path.join(dir, "variants.json"), "utf8");
102
+ const parsed = JSON.parse(raw);
103
+ const ids = (parsed.items ?? []).map((item) => item.id).filter((id) => typeof id === "string" && id.length > 0);
104
+ if (ids.length === 0)
105
+ throw new Error("No variant ids found in catalog variants.json");
106
+ return ids;
107
+ }
@@ -0,0 +1,71 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ // Display order — drives CREATIVE_SURFACE_HINTS and, downstream, surfaceSequence.
5
+ export const SURFACE_REGISTRY = [
6
+ { id: "feed", outcome: "add-feed", label: "Feed and content surface" },
7
+ { id: "comments", outcome: "add-comments", label: "Comments and replies" },
8
+ { id: "chat", outcome: "add-chat", label: "Chat inbox and threads" },
9
+ { id: "profile", outcome: "add-follow", label: "Profile and relationship graph" },
10
+ { id: "community", outcome: "add-community", label: "Community management and identity" },
11
+ { id: "notifications", outcome: "add-notifications", label: "Notifications and activation" },
12
+ ];
13
+ // Scan order for reducing an object list to its surfaces/outcomes. Kept distinct from the display
14
+ // order above to reproduce the pre-derivation if-chain output byte-for-byte (community was scanned
15
+ // before profile). Faithful-reproduction only; could be unified if order is proven immaterial.
16
+ const SURFACE_SCAN_ORDER = ["feed", "comments", "chat", "community", "profile", "notifications"];
17
+ const outcomeBySurface = new Map(SURFACE_REGISTRY.map((s) => [s.id, s.outcome]));
18
+ const VALID_SURFACES = new Set(SURFACE_REGISTRY.map((s) => s.id));
19
+ function catalogRoot() {
20
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
21
+ return path.resolve(moduleDir, "..", "..", "packages", "intelligence", "catalog");
22
+ }
23
+ // object id -> surface / block, in catalog declaration order. Read synchronously at module load so
24
+ // the SOCIAL_PLUS_OBJECTS export and the lookup functions stay synchronous (matches prior contract).
25
+ const objectSurface = new Map();
26
+ const objectBlock = new Map();
27
+ {
28
+ const filePath = path.join(catalogRoot(), "experience-objects.json");
29
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
30
+ for (const item of parsed.items ?? []) {
31
+ if (typeof item.id !== "string")
32
+ continue;
33
+ if (typeof item.surface === "string") {
34
+ if (!VALID_SURFACES.has(item.surface)) {
35
+ throw new Error(`experience object "${item.id}" declares unknown surface "${item.surface}" (expected one of: ${[...VALID_SURFACES].join(", ")})`);
36
+ }
37
+ objectSurface.set(item.id, item.surface);
38
+ }
39
+ if (typeof item.block === "string") {
40
+ objectBlock.set(item.id, item.block);
41
+ }
42
+ }
43
+ }
44
+ // SDK-backed objects are exactly those placed on a surface; app-layer (knowledge/prediction) and
45
+ // in-development objects declare no surface and are excluded.
46
+ export const SOCIAL_PLUS_OBJECTS = new Set(objectSurface.keys());
47
+ export function surfaceIdsForObjects(objectIds) {
48
+ const present = new Set(objectIds);
49
+ const surfacesPresent = new Set();
50
+ for (const id of present) {
51
+ const surface = objectSurface.get(id);
52
+ if (surface)
53
+ surfacesPresent.add(surface);
54
+ }
55
+ return SURFACE_SCAN_ORDER.filter((sid) => surfacesPresent.has(sid));
56
+ }
57
+ export function outcomesForObjects(objectIds) {
58
+ return surfaceIdsForObjects(objectIds).map((sid) => outcomeBySurface.get(sid));
59
+ }
60
+ export function blockIdForObject(objectId) {
61
+ return objectBlock.get(objectId);
62
+ }
63
+ // Object ids bound to a surface, in catalog declaration order (used to build CREATIVE_SURFACE_HINTS).
64
+ export function surfaceObjectIds(surfaceId) {
65
+ const ids = [];
66
+ for (const [id, surface] of objectSurface) {
67
+ if (surface === surfaceId)
68
+ ids.push(id);
69
+ }
70
+ return ids;
71
+ }
package/dist/outcomes.js CHANGED
@@ -2,7 +2,7 @@ export function hasAnswer(answers, id) {
2
2
  const value = answers[id];
3
3
  return typeof value === "string" && value.trim() !== "";
4
4
  }
5
- const CLASSIFY_ORDER = [
5
+ export const CLASSIFY_ORDER = [
6
6
  "setup-push",
7
7
  "setup-live-data",
8
8
  "add-comments",
@@ -1531,7 +1531,7 @@ const unknown = {
1531
1531
  resolveVerification: () => [],
1532
1532
  resolveNotes: () => [],
1533
1533
  };
1534
- const outcomeRegistry = {
1534
+ export const outcomeRegistry = {
1535
1535
  "setup-sdk": setupSdk,
1536
1536
  "setup-push": setupPush,
1537
1537
  "setup-live-data": setupLiveData,
package/dist/server.js CHANGED
@@ -6,11 +6,14 @@ import { fileURLToPath } from "node:url";
6
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
- import { attestRule, attestRuleTool, checkCompliance, checkComplianceTool, explainRule, explainRuleTool, initCompliance, initComplianceTool, initEngagement, initEngagementTool, showEngagement, showEngagementTool, statusCompliance, syncCompliance, syncComplianceTool, } from "./tools/compliance.js";
9
+ import { attestRule, attestRuleTool, checkCompliance, checkComplianceTool, explainRule, explainRuleTool, experienceReportTool, initCompliance, initComplianceTool, initEngagement, initEngagementTool, showEngagement, showEngagementTool, statusCompliance, syncCompliance, syncComplianceTool, } from "./tools/compliance.js";
10
10
  import { designCheckTool, designExtractTool, designInitTokensTool, designPreviewTool, designReferenceTool } from "./tools/design.js";
11
11
  import { getDocPageTool, searchDocsTool } from "./tools/docs.js";
12
+ import { compileExperienceTool } from "./tools/experienceCompiler.js";
13
+ import { experienceSensorsTool } from "./tools/experienceSensors.js";
12
14
  import { planHarnessTool } from "./tools/harness.js";
13
15
  import { planIntegrationTool } from "./tools/integration.js";
16
+ import { recordLearningTool, showLearningTool } from "./tools/learning.js";
14
17
  import { inspectProjectTool, validateSetupTool } from "./tools/project.js";
15
18
  import { resolveRequestTool, suggestPatchTool } from "./tools/resolve.js";
16
19
  import { runSensorsTool } from "./tools/sensors.js";
@@ -18,6 +21,7 @@ import { getSdkFactsTool } from "./tools/sdkFacts.js";
18
21
  import { addBlockInstall, listRegistryBlocks, planBlockInstall, validateBlockInstall } from "./tools/blocks.js";
19
22
  import { debugIssueTool, debugIssue } from "./tools/debug.js";
20
23
  import { creativeAcceptTool, creativeBriefTool } from "./tools/creative.js";
24
+ import { uxHarnessTool } from "./tools/uxHarness.js";
21
25
  import { packageName, packageVersion } from "./version.js";
22
26
  const tools = new Map([
23
27
  searchDocsTool,
@@ -25,10 +29,16 @@ const tools = new Map([
25
29
  inspectProjectTool,
26
30
  creativeBriefTool,
27
31
  creativeAcceptTool,
32
+ uxHarnessTool,
33
+ compileExperienceTool,
34
+ experienceSensorsTool,
28
35
  planHarnessTool,
29
36
  planIntegrationTool,
30
37
  initComplianceTool,
31
38
  checkComplianceTool,
39
+ experienceReportTool,
40
+ recordLearningTool,
41
+ showLearningTool,
32
42
  syncComplianceTool,
33
43
  attestRuleTool,
34
44
  explainRuleTool,
@@ -140,15 +150,18 @@ async function handleCli(args) {
140
150
  if (command === "creative") {
141
151
  if (args[1] === "accept") {
142
152
  const subArgs = args.slice(2);
143
- assertOnlyKnownFlags(subArgs, ["variant", "brief", "brief-path"], "creative accept");
153
+ assertOnlyKnownFlags(subArgs, ["variant", "brief", "brief-path", "rationale", "confidence", "closest"], "creative accept");
144
154
  await printToolResult(creativeAcceptTool, {
145
155
  repoPath: positionalRepoPath(subArgs),
146
- variantId: requiredFlagValue(subArgs, "variant", "creative accept requires --variant <id>."),
156
+ variantId: requiredFlagValue(subArgs, "variant", "creative accept requires --variant <id> (or --variant none to record a catalog gap)."),
147
157
  briefPath: flagValue(subArgs, "brief") ?? flagValue(subArgs, "brief-path"),
158
+ rationale: flagValue(subArgs, "rationale"),
159
+ confidence: flagValue(subArgs, "confidence"),
160
+ closest: flagValue(subArgs, "closest"),
148
161
  });
149
162
  return "exit";
150
163
  }
151
- assertOnlyKnownFlags(args, ["request", "requirements", "prototype", "surface", "surface-path", "no-requirements", "no-write"], "creative");
164
+ assertOnlyKnownFlags(args, ["request", "requirements", "prototype", "surface", "surface-path", "no-requirements", "no-write", "ranking-preview"], "creative");
152
165
  if (hasFlag(args, "no-requirements") && flagValue(args, "requirements")) {
153
166
  throw new Error("creative accepts either --requirements <path> or --no-requirements, not both.");
154
167
  }
@@ -158,10 +171,82 @@ async function handleCli(args) {
158
171
  surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
159
172
  requirementsPath: hasFlag(args, "no-requirements") ? "none" : flagValue(args, "requirements"),
160
173
  prototypePath: flagValue(args, "prototype"),
174
+ rankingPreview: hasFlag(args, "ranking-preview"),
161
175
  write: !hasFlag(args, "no-write"),
162
176
  });
163
177
  return "exit";
164
178
  }
179
+ if (command === "ux-harness") {
180
+ assertOnlyKnownFlags(args, ["surface", "surface-path", "no-write"], "ux-harness");
181
+ await printToolResult(uxHarnessTool, {
182
+ repoPath: positionalRepoPath(args.slice(1)),
183
+ surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
184
+ write: !hasFlag(args, "no-write"),
185
+ });
186
+ return "exit";
187
+ }
188
+ if (command === "experience-report" || command === "experience_report") {
189
+ assertOnlyKnownFlags(args, ["no-write"], "experience-report");
190
+ await printToolResult(experienceReportTool, {
191
+ repoPath: positionalRepoPath(args.slice(1)),
192
+ write: !hasFlag(args, "no-write"),
193
+ });
194
+ return "exit";
195
+ }
196
+ if (command === "learning" || command === "learn") {
197
+ const sub = args[1];
198
+ const subArgs = args.slice(2);
199
+ if (sub === "record") {
200
+ assertOnlyKnownFlags(subArgs, ["kind", "sentiment", "variant", "variant-id", "note", "metric", "no-write"], "learning record");
201
+ await printToolResult(recordLearningTool, {
202
+ repoPath: positionalRepoPath(subArgs),
203
+ kind: flagValue(subArgs, "kind"),
204
+ sentiment: flagValue(subArgs, "sentiment"),
205
+ variantId: flagValue(subArgs, "variant") ?? flagValue(subArgs, "variant-id"),
206
+ note: flagValue(subArgs, "note"),
207
+ metrics: keyValueFlag(subArgs, "metric"),
208
+ write: !hasFlag(subArgs, "no-write"),
209
+ });
210
+ return "exit";
211
+ }
212
+ if (sub === "show" || sub === "summary") {
213
+ assertOnlyKnownFlags(subArgs, [], `learning ${sub}`);
214
+ await printToolResult(showLearningTool, {
215
+ repoPath: positionalRepoPath(subArgs),
216
+ });
217
+ return "exit";
218
+ }
219
+ console.error(`Unknown learning subcommand: ${sub ?? "(none)"}. Expected "record" or "show".`);
220
+ process.exitCode = 1;
221
+ return "exit";
222
+ }
223
+ if (command === "experience") {
224
+ const sub = args[1];
225
+ const subArgs = args.slice(2);
226
+ if (sub === "compile") {
227
+ assertOnlyKnownFlags(subArgs, ["request", "surface", "surface-path", "registry", "no-write"], "experience compile");
228
+ await printToolResult(compileExperienceTool, {
229
+ repoPath: positionalRepoPath(subArgs),
230
+ request: flagValue(subArgs, "request"),
231
+ surfacePath: flagValue(subArgs, "surface") ?? flagValue(subArgs, "surface-path"),
232
+ registryPath: flagValue(subArgs, "registry"),
233
+ write: !hasFlag(subArgs, "no-write"),
234
+ });
235
+ return "exit";
236
+ }
237
+ if (sub === "sensors") {
238
+ assertOnlyKnownFlags(subArgs, ["surface", "surface-path", "no-write"], "experience sensors");
239
+ await printToolResult(experienceSensorsTool, {
240
+ repoPath: positionalRepoPath(subArgs),
241
+ surfacePath: flagValue(subArgs, "surface") ?? flagValue(subArgs, "surface-path"),
242
+ write: !hasFlag(subArgs, "no-write"),
243
+ });
244
+ return "exit";
245
+ }
246
+ console.error(`Unknown experience subcommand: ${sub ?? "(none)"}. Expected "compile" or "sensors".`);
247
+ process.exitCode = 1;
248
+ return "exit";
249
+ }
165
250
  if (command === "debug") {
166
251
  assertOnlyKnownFlags(args, ["error", "error-file", "brief"], "debug");
167
252
  let errorMessage = flagValue(args, "error");
@@ -231,7 +316,7 @@ async function handleCli(args) {
231
316
  return "exit";
232
317
  }
233
318
  if (command === "run-sensors" || command === "run_sensor" || command === "run-sensor") {
234
- await printToolResult(runSensorsTool, {
319
+ const printed = await printToolResult(runSensorsTool, {
235
320
  repoPath: positionalRepoPath(args.slice(1)),
236
321
  request: flagValue(args, "request"),
237
322
  include: flagValues(args, "include"),
@@ -239,6 +324,13 @@ async function handleCli(args) {
239
324
  dryRun: hasFlag(args, "dry-run"),
240
325
  surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
241
326
  });
327
+ // Reflect sensor outcome in the exit code so `vise run-sensors` is usable as a CI gate.
328
+ // Previously it always exited 0, so a failed/timed-out build silently read as green.
329
+ // dry-run / passed / no-sensors stay 0; only real failures fail.
330
+ const status = toolResultStatus(printed);
331
+ if (status === "failed" || status === "timed-out") {
332
+ process.exitCode = 1;
333
+ }
242
334
  return "exit";
243
335
  }
244
336
  if (command === "resolve") {
@@ -490,13 +582,74 @@ Usage:
490
582
  vise creative [repoPath] --request "Add engagement" --requirements none
491
583
  vise creative [repoPath] --request "Add engagement" --no-requirements
492
584
  vise creative [repoPath] --request "Add engagement" --prototype ./prototype.html
585
+ vise creative [repoPath] --request "Add engagement" --ranking-preview
493
586
  vise creative [repoPath] --request "Add engagement" --no-write
494
587
  vise creative accept [repoPath] --variant community-first
588
+ vise creative accept [repoPath] --variant none --rationale "No variant fits X; closest is Y" --closest discovery-first
495
589
 
496
590
  Output:
497
591
  Writes sp-vise/creative-brief.json and sp-vise/creative-brief.md unless --no-write is set.
592
+ --ranking-preview also writes sp-vise/candidate-ranking-preview.json as an opt-in advisory local preview.
498
593
  Requirements are optional; absence or explicit none switches creative mode to exploratory.
499
- creative accept writes sp-vise/creative-selection.json for plan/init/workplan feed-forward.`;
594
+ creative accept writes sp-vise/creative-selection.json for plan/init/workplan feed-forward.
595
+ creative accept --variant none records sp-vise/catalog-gap.json (a local no-fit signal for human catalog review).`;
596
+ }
597
+ if (command === "ux-harness") {
598
+ return `${packageName} ux-harness
599
+
600
+ Generate the advisory UX Harness sidecar from an accepted creative selection.
601
+
602
+ Usage:
603
+ vise ux-harness [repoPath]
604
+ vise ux-harness [repoPath] --surface apps/web
605
+ vise ux-harness [repoPath] --no-write
606
+
607
+ Output:
608
+ Writes sp-vise/ux-harness.json by default. The sidecar contains selected UX pattern expectations, tradeoffs, anti-patterns, and advisory conformance hints. It never changes deterministic compliance rules by itself.`;
609
+ }
610
+ if (command === "experience-report" || command === "experience_report") {
611
+ return `${packageName} experience-report
612
+
613
+ Generate the advisory dimensioned Experience Report from current compliance evidence.
614
+
615
+ Usage:
616
+ vise experience-report [repoPath]
617
+ vise experience-report [repoPath] --no-write
618
+
619
+ Output:
620
+ Writes sp-vise/experience-report.json by default. The report summarizes technical compliance, design contract presence, UX Harness advisory evidence, and business alignment without producing a calibrated single score.`;
621
+ }
622
+ if (command === "learning" || command === "learn") {
623
+ return `${packageName} learning
624
+
625
+ Record and review local-only Engagement Intelligence learning events.
626
+
627
+ Usage:
628
+ vise learning record [repoPath] --sentiment positive --note "Customer preferred Community First"
629
+ vise learning record [repoPath] --kind outcome-snapshot --metric retention_delta=0.12
630
+ vise learning record [repoPath] --kind report-review --sentiment neutral --no-write
631
+ vise learning show [repoPath]
632
+
633
+ Output:
634
+ record writes sp-vise/learning-events.jsonl and sp-vise/learning-summary.json by default. Learning events are local evidence only; Vise does not change recommendation ranking from them yet.`;
635
+ }
636
+ if (command === "experience") {
637
+ return `${packageName} experience
638
+
639
+ Compile an accepted Engagement Intelligence variant and summarize experience sensors.
640
+
641
+ Usage:
642
+ vise experience compile [repoPath]
643
+ vise experience compile [repoPath] --request "Make this app social"
644
+ vise experience compile [repoPath] --registry ./registry/blocks.json
645
+ vise experience compile [repoPath] --surface apps/web --no-write
646
+ vise experience sensors [repoPath]
647
+ vise experience sensors [repoPath] --surface apps/web --no-write
648
+
649
+ Output:
650
+ compile writes sp-vise/experience-compiler.json by default. The artifact summarizes install guidance, navigation wiring, per-surface plan/init commands, UX expectations, optional Block Factory bridge candidates, design adaptation, and validation commands. It does not edit source code.
651
+
652
+ sensors writes sp-vise/experience-sensors.json by default. The artifact summarizes advisory technical, design, UX, accessibility, and business-alignment evidence without changing vise check exit codes or producing a calibrated score.`;
500
653
  }
501
654
  if (command === "plan-harness") {
502
655
  return `${packageName} plan-harness
@@ -745,7 +898,13 @@ Usage:
745
898
  vise print-skill Print bundled skill markdown
746
899
  vise inspect [repoPath] Inspect platform and design signals
747
900
  vise creative [repoPath] --request "..." Create an Engagement Intelligence brief
748
- vise creative accept [repoPath] --variant <id> Accept a creative variant
901
+ vise creative accept [repoPath] --variant <id|none> Accept a creative variant, or record a no-fit catalog gap with --variant none
902
+ vise ux-harness [repoPath] Generate UX Harness expectations from creative selection
903
+ vise experience-report [repoPath] Write the advisory dimensioned Experience Report
904
+ vise experience compile [repoPath] Compile selected variant into implementation artifacts
905
+ vise experience sensors [repoPath] Write advisory experience sensor framework
906
+ vise learning record [repoPath] Record a local-only learning event
907
+ vise learning show [repoPath] Show local learning summary
749
908
  vise debug [repoPath] --error ... Debug an SDK-specific runtime error and emit a repair brief
750
909
  vise plan [repoPath] --request "..." Create an implementation plan
751
910
  vise workplan next [repoPath] --request "..." Get the next broad-social surface to implement
@@ -784,7 +943,19 @@ SOCIAL_PLUS_DOCS_ROOT to test against a local social-plus-docs checkout.`;
784
943
  }
785
944
  async function printToolResult(tool, input) {
786
945
  const result = await tool.call(input);
787
- console.log(result.content.map((item) => item.text).join("\n"));
946
+ const text = result.content.map((item) => item.text).join("\n");
947
+ console.log(text);
948
+ return { result, text };
949
+ }
950
+ // Parse the `status` field from a tool's printed JSON payload (best-effort).
951
+ function toolResultStatus(printed) {
952
+ try {
953
+ const payload = JSON.parse(printed.text);
954
+ return typeof payload.status === "string" ? payload.status : undefined;
955
+ }
956
+ catch {
957
+ return undefined;
958
+ }
788
959
  }
789
960
  async function installSkill(args) {
790
961
  const source = skillSourceDir();
@@ -1021,6 +1192,7 @@ async function workplanStatus(args) {
1021
1192
  request,
1022
1193
  progressPath: workplanProgressPath(repoRoot),
1023
1194
  creativeContext: plan.socialWorkplan?.creativeContext ?? plan.creativeContext,
1195
+ uxHarness: plan.socialWorkplan?.uxHarness ?? plan.uxHarness,
1024
1196
  completed,
1025
1197
  sequence: sequence.map((surface) => ({
1026
1198
  id: surface.id,
@@ -1037,6 +1209,7 @@ async function workplanStatus(args) {
1037
1209
  label: nextSurface.label,
1038
1210
  intake: nextSurface.intake,
1039
1211
  validation: nextSurface.validation,
1212
+ uxHarness: nextSurface.uxHarness,
1040
1213
  commands: {
1041
1214
  plan: nextSurface.planCommand,
1042
1215
  init: nextSurface.initCommand,
@@ -1222,7 +1395,7 @@ function ciCheckResult(result) {
1222
1395
  };
1223
1396
  }
1224
1397
  function positionalRepoPath(args) {
1225
- const flagsWithValues = new Set(["request", "requirements", "prototype", "variant", "brief", "brief-path", "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"]);
1398
+ const flagsWithValues = new Set(["request", "requirements", "prototype", "variant", "variant-id", "brief", "brief-path", "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", "kind", "sentiment", "metric"]);
1226
1399
  for (let index = 0; index < args.length; index += 1) {
1227
1400
  const arg = args[index];
1228
1401
  if (!arg) {