@amityco/social-plus-vise 0.14.26 → 0.14.28
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 +59 -0
- package/README.md +25 -7
- package/dist/capabilities.js +45 -1
- package/dist/intelligence/grounding.js +107 -0
- package/dist/intelligence/placement.js +71 -0
- package/dist/outcomes.js +2 -2
- package/dist/server.js +189 -10
- package/dist/tools/ast.js +161 -9
- package/dist/tools/blocks.js +95 -2
- package/dist/tools/compliance.js +176 -15
- package/dist/tools/creative.js +542 -106
- package/dist/tools/experienceCompiler.js +439 -0
- package/dist/tools/experienceSensors.js +406 -0
- package/dist/tools/harness.js +141 -8
- package/dist/tools/integration.js +43 -2
- package/dist/tools/learning.js +486 -0
- package/dist/tools/project.js +147 -17
- package/dist/tools/sdkFacts.js +83 -6
- package/dist/tools/sensors.js +38 -0
- package/dist/tools/uxHarness.js +546 -0
- package/package.json +45 -9
- package/packages/intelligence/README.md +1 -1
- package/packages/intelligence/catalog/archetypes.json +10 -0
- package/packages/intelligence/catalog/business-goals.json +8 -0
- package/packages/intelligence/catalog/catalog.schema.json +224 -0
- package/packages/intelligence/catalog/experience-objects.json +18 -18
- package/packages/intelligence/catalog/variants.json +56 -7
- package/scripts/catalog-coverage-html.mjs +325 -0
- package/scripts/catalog-relationships-html.mjs +686 -0
- package/scripts/catalog-sheets.mjs +286 -0
- package/scripts/extract-sdk-models.mjs +408 -0
- package/scripts/import-sdk-surface.mjs +43 -3
- package/scripts/pilot-feedback.mjs +107 -0
- package/scripts/workshop-board-html.mjs +1018 -0
- package/scripts/workshop-kit.mjs +252 -0
- package/sdk-surface/manifest.json +48 -2
- package/sdk-surface/models.android.json +990 -0
- package/sdk-surface/models.flutter.json +980 -0
- package/sdk-surface/models.ios.json +980 -0
- package/sdk-surface/models.typescript.json +1304 -0
- package/skills/social-plus-vise/SKILL.md +1 -1
- package/skills/vise-harness-engineer/SKILL.md +1 -1
- package/social.plus-vise.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,62 @@ 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.28 — 2026-06-10
|
|
8
|
+
|
|
9
|
+
**Theme:** block-aware completeness — install a block, end green. Plus platform-leg hardening: Swift gets the tree-sitter treatment and iOS gets a guarded build sensor.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Field-level SDK model facts (sdk-facts Phase 4):** `vise sdk-facts` / `get_sdk_facts` no longer prove symbol existence only. The bundled snapshot now includes `sdk-surface/models.<platform>.json` (wire format `2026-06-10.sdk-model-facts.v1`, owned by `packages/schemas/model-facts.schema.json` with a type-only TS twin) carrying capability-anchored model schemas — per field: `name`, `type` (the platform's own string form), `optional`, `memberKind`, and `declaredIn` source anchor — plus full extraction provenance (extractor id/version, timestamp, upstream docs-ops extractor, upstream git commit). `--include-models` (MCP `includeModels`) serves the schemas; capability model entries gain a verbatim `schema`; every result carries a `modelFacts` status so absence is explicit.
|
|
13
|
+
- **Extraction-grounded by construction:** `scripts/extract-sdk-models.mjs` (run by `sdk-surface:import`) grounds each platform mechanically — TypeScript via the compiler API over the local SDK sources (`names-and-types`, including `?` optionality and file:line), Android via dokka signature data (`names-and-types`, Kotlin `?` nullability, doc-prose stripped), iOS/Flutter as `names-only` because their docc-abi/dart exports carry no type data (the schema then forces field `type`/`optional` to `null` — fabricated detail cannot validate). A platform whose field source is unreachable ships no model file and the manifest records why. Scope is limited to the first factory capabilities (comments, reactions, posts, users): npm tarball grew 479.2 kB → 484.2 kB.
|
|
14
|
+
- **Reaction model anchors:** the `reactions` capability now also anchors `Amity.Reactor` and `Amity.Reactable` (the reaction-summary shape blocks actually consume), alongside `Amity.Reaction`.
|
|
15
|
+
|
|
16
|
+
### Verified (model facts)
|
|
17
|
+
- `test:sdk-surface` locks the manifest `models` map (sha256, per-platform grounding) and one real extracted model per platform — including that `Amity.Comment` carries `userId` but **not** the imagined `creatorId`/`isHidden` fields that previously lived in hand-written factory fixtures.
|
|
18
|
+
- `test:sdk-facts` covers `modelFacts` status, capability schema attachment, `includeModels` capability filtering, names-only null enforcement, and the CLI `--include-models` path.
|
|
19
|
+
- The schemas package self-test validates every committed snapshot against `model-facts.schema.json` and proves the schema bites (11 doctored snapshots fail, including fabricated types under names-only grounding).
|
|
20
|
+
- **Swift AST validators (tree-sitter-swift 0.7.x):** the highest-false-positive-risk iOS rules move from regex to the same tree-sitter helper layer Kotlin uses (`tree-sitter-swift` is the maintained alex-pinkus grammar; its node shapes descend from the Kotlin grammar, so `findCallExpressions` shares the walk and adds Swift labeled-argument extraction via the new `pickLabeledArgument`). Migrated rules — ids, severities, and pass/fail semantics unchanged, only precision improved:
|
|
21
|
+
- `ios.auth.no-literal-user-id`, `ios.secret.inline-api-key`, `ios.feed.target.literal` now resolve **indirect literals** (`let FALLBACK_USER = "demo-user-42"` … `login(userId: FALLBACK_USER)`) that the call-site regex could never see — proven by the new `ios-userid-via-constant` / `ios-secret-via-constant` / `ios-feed-target-via-constant` fixtures.
|
|
22
|
+
- `ios.session-handler.retained` asks the real scope question (is the binding inside a function body?) instead of bridging 200 chars from any `func … {`. A bare class-scope `let sessionHandler = …` directly after a short function used to false-fire (the rescue pattern demanded an access modifier); the new `ios-session-handler-class-scope-bare` fixture locks the fix. Type-annotated locals (`let h: AmitySessionHandler = …`) the regex bridge missed are now caught.
|
|
23
|
+
- `ios.user.ban-state-respected` detects its interaction surface on the comment-stripped view — `// TODO: wire createPost here` no longer counts as an interaction (new `ios-ban-state-comment-only` fixture); the `// vise: ban state checked at` escape hatch deliberately stays on raw text.
|
|
24
|
+
- All Swift gate rules now use the precise tree-sitter comment stripper, chained onto the conservative scanner so bindings-unavailable/oversized files degrade to the previous behavior, never to raw text. `ios-happy-path` stays at zero findings (FP canary), and a missing `tree-sitter-swift` prebuild degrades only the Swift helpers — ts/tsx/kotlin AST stays up.
|
|
25
|
+
- **Dart AST honestly skipped:** every Dart grammar on npm fails against the pinned `tree-sitter@^0.21` (stale nan binding, ≥0.22-only API, forked core, WASM-only, or an unmaintained vendor-internal fork). Dart rules remain regex + conservative scanner; the verdict and re-evaluation trigger are recorded in ARCHITECTURE.md.
|
|
26
|
+
- **iOS build sensor — guarded best-effort (revises the old "xcodebuild is too fragile to wire" decision):** an `.xcodeproj`/`.xcworkspace` at the surface root now enables two sensors, **only when `xcodebuild` is on PATH** — absent, `vise run-sensors` reports the sensor `skipped` with the install-Xcode precondition (the pf-003 visible-precondition pattern) instead of returning no-sensors. When runnable: a cheap `xcodebuild -list` integrity probe, then a bounded-timeout build with signing disabled (`CODE_SIGNING_ALLOWED=NO`/`CODE_SIGNING_REQUIRED=NO`/`CODE_SIGN_IDENTITY=`). Non-zero exits are classified before being reported: environment-caused failures (Command-Line-Tools-only `xcode-select`, unaccepted Xcode license, signing/provisioning demands, missing simulator/SDK destination, undeterminable workspace scheme) become **skipped-with-reason and exit 0** — never project failure — while real compile/link errors stay `failed` with a non-zero CLI exit (pf-004-era semantics). The classification channel is a new serializable `environmentSkips` field on command sensors, available to any future toolchain with the same exit-code conflation. Locked by a stub-`xcodebuild` controlled-PATH matrix in `test:sensors` (detected+green, detected+real-failure, environmental skips, absent toolchain).
|
|
27
|
+
- **Installed blocks satisfy completeness baselines:** `vise check` previously scanned customer source only, so a capability delivered inside an installed Block Factory package (e.g. the Comments block's composer) still reported `completeness-gap` (exit 5). Now a checklist capability counts as present with evidence `source: "block:<blockId>"` (and a note naming the block) when an installed sidecar entry declares it and the install is still locally valid.
|
|
28
|
+
- **Factory declares, Vise validates:** Block Factory contracts declare `providesCapabilities` using Vise's completeness-checklist id vocabulary (Vise owns the id space, the factory owns the per-block claim; an empty list is legitimate — the Reactions block provides no baseline ids). `vise blocks plan`/`add` carry the registry entry's ids into the install plan and `--apply` records them — plus the manifest `dependencyName` — in `sp-vise/blocks.json` (sidecar `schemaVersion` bumped to `2026-06-10.vise-blocks-sidecar.v1`).
|
|
29
|
+
- **Seam guard:** `vise blocks plan`/`add`/`validate` emit a `blocks.providesCapabilities.known` **warning** finding (never a failure) for declared ids outside Vise's completeness vocabulary; unknown ids never satisfy a gap.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- **Skill: bounded-turn stop guidance.** Agents running in one-shot/print mode are told to reach a `vise check` verdict before the turn ends rather than ending mid-environment-setup (finding from the first semantic harness-E2E measurement).
|
|
33
|
+
- **Gap returns on drift:** the block evidence is registry-free and re-validated on every `vise check` — the block's dependency must still be declared in the project manifest and every recorded `filesTouched` path must exist. Remove the touched source file or the package and the capability reverts to the normal `completeness-gap`. Pre-`0.14.28` sidecar entries carry no package evidence and fail closed.
|
|
34
|
+
|
|
35
|
+
### Verified
|
|
36
|
+
- `test:ast` extended with the Swift section: grammar availability, labeled-argument call extraction, identifier→literal resolution (with the env-fallback `?? ""` and interpolated-string non-resolution cases), comment stripping that preserves `//` inside strings, function-local vs type-scope handler declarations, the three indirect-literal fixtures, and the two false-positive-kill fixtures (each verified to fire under the pre-change regexes). `ios-happy-path` asserted at zero findings; existing `ios-session-handler-local-var`/`-retained` pass/fail semantics asserted unchanged.
|
|
37
|
+
- `test:sensors` gains the stub-`xcodebuild` controlled-PATH matrix: detection with/without the toolchain, detected+green (exit 0), detected+real-build-failure (exit 1, not environment-skipped), no-simulator and CLT-only environmental skips (exit 0), and the absent-toolchain skip end to end.
|
|
38
|
+
- New `test:blocks-completeness` gate: real `blocks add --apply` against the monorepo Block Factory registry, init with answers, `check --ci` green via `block:comments` evidence, gap restored after deleting a touched file and after removing the manifest dependency, empty `providesCapabilities` tolerated end to end, and the unknown-id warning on a doctored registry.
|
|
39
|
+
- The Block Factory react target-project journey (`validate:target-projects:react`) now asserts the full install journey ends **green** with `comment-composer` satisfied by `block:comments`.
|
|
40
|
+
|
|
41
|
+
### Internal
|
|
42
|
+
- **Registry seam contract extracted to `packages/schemas`:** the block-registry wire format and the `sp-vise/blocks.json` sidecar shape now live in the root workspace package `@amityco/social-plus-schemas` (private, unpublished) — a JSON Schema (`registry.schema.json`) enforced on the factory's `validate:registry`, plus the TypeScript types formerly hand-written in `src/tools/blocks.ts`. Vise imports them **type-only** via a `workspace:*` devDependency, so nothing from the package survives into the published `dist/` (proven by the packed-tarball E2E). Type-only refactor; no behavior change.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- **Attestation evidence fingerprints no longer truncate prose-embedded paths:** the source-path extractor's extension alternation was shortest-first, so `.tsx`→`.ts`, `.json`→`.js`, and `.kts`→`.kt` inside evidence prose silently recorded no fingerprint (weakening drift detection for that attestation). Found by the harness E2E attestation stage, which now cites paths in prose as the standing regression test.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## 0.14.27 — 2026-06-10
|
|
49
|
+
|
|
50
|
+
**Theme:** social-plus-forge monorepo move. No runtime behavior changes.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- **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.
|
|
54
|
+
- **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).
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- **`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.
|
|
58
|
+
|
|
59
|
+
### Docs
|
|
60
|
+
- Backfilled the `vise blocks` installer namespace into the `0.14.5` changelog entry (shipped there, never recorded).
|
|
61
|
+
- `RELEASE.md` no longer hardcodes the current npm version; `RULES.md`/`ARCHITECTURE.md` defer exact rule counts to the rule-coverage gate.
|
|
62
|
+
|
|
7
63
|
## 0.14.26 — 2026-06-06
|
|
8
64
|
|
|
9
65
|
### Added
|
|
@@ -217,6 +273,9 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/
|
|
|
217
273
|
|
|
218
274
|
**Theme:** Optional feed capabilities become explicit opt-in sensors.
|
|
219
275
|
|
|
276
|
+
### Added
|
|
277
|
+
- *(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.
|
|
278
|
+
|
|
220
279
|
### Changed
|
|
221
280
|
- **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
281
|
- **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
|
@@ -77,14 +77,16 @@ 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, 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 |
|
|
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, satisfied by an installed Block Factory block (evidence `source: "block:<id>"`, re-validated locally on every check), or validly opted out; selected optional capabilities run separate sensors |
|
|
81
81
|
|
|
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).
|
|
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. Installed Block Factory blocks count too: the factory declares the completeness ids a block provides (`providesCapabilities`), `vise blocks add --apply` records them in `sp-vise/blocks.json`, and `vise check` honours them only while the block's manifest dependency and touched files are intact — on drift the gap returns. 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
|
|
|
84
84
|
### Engagement Intelligence roadmap
|
|
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
|
|
|
@@ -228,7 +232,7 @@ The benchmark suite is intentionally reported with boundaries:
|
|
|
228
232
|
| **React Native** | ✅ Full | `tsc`, `npm lint`, SDK import smoke |
|
|
229
233
|
| **Flutter / Dart** | ✅ Full | `flutter analyze`, `flutter test` |
|
|
230
234
|
| **Android (Kotlin)** | ✅ Full | Gradle assemble, unit tests |
|
|
231
|
-
| **iOS (Swift)** | ✅ Full | Static rule checks fully operational. Build sensor
|
|
235
|
+
| **iOS (Swift)** | ✅ Full | Static rule checks fully operational (highest-risk rules use the Swift tree-sitter AST). Build sensor is **guarded best-effort**: an `.xcodeproj`/`.xcworkspace` enables an `xcodebuild -list` integrity probe plus a signing-disabled build — only when `xcodebuild` is on PATH (absent → skipped with the visible precondition). Environment-caused failures (no simulator runtime, Command-Line-Tools-only `xcode-select`, signing demands, unaccepted license) report **skipped-with-reason**, never project failure; a real build failure still exits non-zero. |
|
|
232
236
|
|
|
233
237
|
Each platform has dozens of rules across 10 compliance domains (feed, comments, moderation, chat, secrets, session & auth, notifications, live objects, logging hygiene, design tokens).
|
|
234
238
|
|
|
@@ -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
|
|
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. |
|
package/dist/capabilities.js
CHANGED
|
@@ -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"],
|
|
@@ -1187,6 +1187,50 @@ function baselineCapabilities(outcome) {
|
|
|
1187
1187
|
export function capabilityChecklist(outcome) {
|
|
1188
1188
|
return baselineCapabilities(outcome).map((c) => ({ id: c.id, label: c.label, hint: c.hint }));
|
|
1189
1189
|
}
|
|
1190
|
+
/**
|
|
1191
|
+
* The Vise-owned completeness id vocabulary — every id a Block Factory contract
|
|
1192
|
+
* may reference in `providesCapabilities`. Vise owns this id space; the factory
|
|
1193
|
+
* owns the per-block declaration (see block-factory/docs/vise-boundary.md).
|
|
1194
|
+
*/
|
|
1195
|
+
export function completenessCapabilityIds() {
|
|
1196
|
+
return new Set(CAPABILITIES.map((capability) => capability.id));
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Overlay installed-block evidence onto a source-scan completeness assessment.
|
|
1200
|
+
* A still-missing checklist capability becomes present with evidence
|
|
1201
|
+
* `source: "block:<blockId>"` when a locally validated installed block declares
|
|
1202
|
+
* it in `providesCapabilities`. Callers are responsible for the local validation
|
|
1203
|
+
* (package declared + filesTouched intact); on drift they must simply not pass
|
|
1204
|
+
* the capability here, so the normal gap returns.
|
|
1205
|
+
*/
|
|
1206
|
+
export function applyBlockProvidedCompleteness(assessment, provided) {
|
|
1207
|
+
if (provided.length === 0 || assessment.missing.length === 0) {
|
|
1208
|
+
return assessment;
|
|
1209
|
+
}
|
|
1210
|
+
const blockByCapability = new Map();
|
|
1211
|
+
for (const item of provided) {
|
|
1212
|
+
if (!blockByCapability.has(item.capabilityId)) {
|
|
1213
|
+
blockByCapability.set(item.capabilityId, item.blockId);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const present = [...assessment.present];
|
|
1217
|
+
const missing = [];
|
|
1218
|
+
for (const item of assessment.missing) {
|
|
1219
|
+
const blockId = blockByCapability.get(item.id);
|
|
1220
|
+
if (blockId) {
|
|
1221
|
+
present.push({
|
|
1222
|
+
id: item.id,
|
|
1223
|
+
label: item.label,
|
|
1224
|
+
source: `block:${blockId}`,
|
|
1225
|
+
note: `Satisfied by installed block "${blockId}" (sp-vise/blocks.json): the block package delivers this capability and its local install validation passed.`,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
missing.push(item);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return { ...assessment, present, missing };
|
|
1233
|
+
}
|
|
1190
1234
|
/** Optional feed-forward choices. These are not baseline completeness requirements. */
|
|
1191
1235
|
export function optionalCapabilityChecklist(outcome, availableIds) {
|
|
1192
1236
|
const available = availableIds ? new Set(availableIds) : undefined;
|
|
@@ -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,
|