From a7777232275da80d3f7f546b3562b070c0879f36 Mon Sep 17 00:00:00 2001 From: Akula Uday Date: Thu, 23 Jul 2026 13:03:27 +0530 Subject: [PATCH 1/4] docs(sdd): bootstrap SDD foundation for components Co-authored-by: Cursor --- .gitignore | 4 + .sdd/manifest.json | 268 +++++++++++++++++ AGENTS.md | 106 +++++++ ai-docs/ARCHITECTURE.md | 153 ++++++++++ ai-docs/CONTRACTS.md | 83 ++++++ ai-docs/GETTING_STARTED.md | 50 ++++ ai-docs/GLOSSARY.md | 31 ++ ai-docs/REVIEW_CHECKLIST.md | 41 +++ ai-docs/RULES.md | 67 +++++ ai-docs/SECURITY.md | 56 ++++ ai-docs/SPEC_INDEX.md | 41 +++ ai-docs/patterns/co-located-tests.md | 35 +++ ai-docs/patterns/with-adapter-injection.md | 37 +++ ai-docs/patterns/wxc-class-prefix.md | 40 +++ src/adapters/ai-docs/adapters-spec.md | 264 +++++++++++++++++ src/components/ai-docs/components-spec.md | 328 +++++++++++++++++++++ src/styles/ai-docs/styles-themes-spec.md | 230 +++++++++++++++ 17 files changed, 1834 insertions(+) create mode 100644 .sdd/manifest.json create mode 100644 AGENTS.md create mode 100644 ai-docs/ARCHITECTURE.md create mode 100644 ai-docs/CONTRACTS.md create mode 100644 ai-docs/GETTING_STARTED.md create mode 100644 ai-docs/GLOSSARY.md create mode 100644 ai-docs/REVIEW_CHECKLIST.md create mode 100644 ai-docs/RULES.md create mode 100644 ai-docs/SECURITY.md create mode 100644 ai-docs/SPEC_INDEX.md create mode 100644 ai-docs/patterns/co-located-tests.md create mode 100644 ai-docs/patterns/with-adapter-injection.md create mode 100644 ai-docs/patterns/wxc-class-prefix.md create mode 100644 src/adapters/ai-docs/adapters-spec.md create mode 100644 src/components/ai-docs/components-spec.md create mode 100644 src/styles/ai-docs/styles-themes-spec.md diff --git a/.gitignore b/.gitignore index 106dcece0..8d48cdb40 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,8 @@ reports/ !package.json !package-lock.json !src/data/*.json +!.sdd/manifest.json node_modules/ + +# SDD temporary skill outputs +.generated/ diff --git a/.sdd/manifest.json b/.sdd/manifest.json new file mode 100644 index 000000000..2c8f8f018 --- /dev/null +++ b/.sdd/manifest.json @@ -0,0 +1,268 @@ +{ + "manifest_version": 1, + "repository": { + "name": "webex/components", + "purpose": "Published React component library for embedding Webex-styled meeting, messaging, and people UI with adapter-injected data.", + "category": "cat1-legacy", + "primary_languages": ["javascript"] + }, + "topology": "Single-repo", + "commands": { + "install": { + "command": "npx npm-install-peers", + "source_file": "CONTRIBUTING.md" + }, + "build": { + "command": "npm run build", + "source_file": "package.json" + }, + "test": { + "command": "npm run test", + "source_file": "package.json" + }, + "lint": { + "command": "npm run linter", + "source_file": "package.json" + }, + "coverage": { + "command": "npm run test:coverage", + "source_file": "package.json" + }, + "dev": { + "command": "npm run storybook", + "source_file": "package.json" + } + }, + "coverage_status_definitions": { + "specced": ">=80% public surface specced, drift <5% — spec is authoritative", + "partial": "40-80% specced — spec is a hint, cross-check code", + "untracked": "<40% specced — code is the source of truth" + }, + "modules": [ + { + "path": "src/components/", + "coverage_status": "Specced", + "coverage_evidence": "96% field score assessed 2026-07-23; all exports, hooks catalog, internal components, HOCs, and conventions documented", + "canonical_spec": "src/components/ai-docs/components-spec.md", + "contracts": { + "provides": [ + "WebexMeeting, WebexMessaging, and other exported React components", + "withAdapter HOC and WebexDataProvider", + "AdapterContext and meeting hooks" + ], + "requires": [ + "@webex/component-adapter-interfaces adapter instances", + "react, react-dom, prop-types, rxjs peer dependencies", + "compiled CSS from styles-themes module" + ] + }, + "last_assessed": "2026-07-23", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": false, + "enforces_domain_rules": false, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": false, + "has_design_tradeoff": false, + "stateful_transitions": true, + "exposes_wire_protocol": false, + "ui_multi_screen": true, + "large_data_model": false, + "has_tiers": false, + "module_specific_conventions": true, + "published_package": true, + "embedded_in_host": true, + "holds_client_state": true, + "resolved_by": "cursor-agent-session (bootstrap questionnaire)", + "resolved_at": "2026-07-23T12:45:00Z" + } + }, + { + "path": "src/adapters/", + "coverage_status": "Specced", + "coverage_evidence": "97% field score assessed 2026-07-23; all domain adapters, meeting controls registry, and conventions documented", + "canonical_spec": "src/adapters/ai-docs/adapters-spec.md", + "contracts": { + "provides": [ + "WebexJSONAdapter façade", + "Domain JSON adapters: meetings, people, rooms, activities, memberships, organizations" + ], + "requires": [ + "@webex/component-adapter-interfaces", + "rxjs", + "JSON datasource with activities, meetings, memberships, organizations, people, rooms keys" + ] + }, + "last_assessed": "2026-07-23", + "section_profile": { + "has_ui": false, + "crosses_service_boundaries": false, + "enforces_domain_rules": false, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": false, + "has_design_tradeoff": false, + "stateful_transitions": true, + "exposes_wire_protocol": false, + "ui_multi_screen": false, + "large_data_model": false, + "has_tiers": false, + "module_specific_conventions": false, + "published_package": true, + "embedded_in_host": true, + "holds_client_state": true, + "resolved_by": "cursor-agent-session (bootstrap questionnaire)", + "resolved_at": "2026-07-23T12:45:00Z" + } + }, + { + "path": "src/styles/", + "coverage_status": "Specced", + "coverage_evidence": "95% field score assessed 2026-07-23; SCSS registry, themes, fonts, build outputs, and conventions documented", + "canonical_spec": "src/styles/ai-docs/styles-themes-spec.md", + "contracts": { + "provides": [ + "dist/css/webex-components.css", + "dist/themes/dark and dist/themes/light assets", + "dist/assets/fonts" + ], + "requires": [ + "component SCSS partials", + "rollup-plugin-scss and rollup-plugin-copy" + ] + }, + "last_assessed": "2026-07-23", + "section_profile": { + "has_ui": false, + "crosses_service_boundaries": false, + "enforces_domain_rules": false, + "is_concurrent_async": false, + "owns_persistence": false, + "returns_caller_errors": false, + "has_design_tradeoff": false, + "stateful_transitions": false, + "exposes_wire_protocol": false, + "ui_multi_screen": false, + "large_data_model": false, + "has_tiers": false, + "module_specific_conventions": true, + "published_package": true, + "embedded_in_host": true, + "holds_client_state": false, + "resolved_by": "cursor-agent-session (bootstrap questionnaire)", + "resolved_at": "2026-07-23T12:45:00Z" + } + } + ], + "spec_policy": { + "delta_grammar": { + "added": "## ADDED Requirements", + "modified": "## MODIFIED Requirements", + "removed": "## REMOVED Requirements" + }, + "require_what_and_why": true, + "require_provenance": true, + "protected_specs": [], + "required_sections_by_change_class": { + "core_always": [ + "Intent (WHAT)", + "Rationale (WHY)", + "Scope/Out-of-scope", + "Acceptance criteria", + "Contracts delta" + ], + "security_or_contract_or_perf_critical": [ + "Data", + "Error Matrix", + "Resilience", + "Observability", + "Operations" + ] + } + }, + "validation": { + "generator_runtime": "cursor-agent-session", + "generator_model": "composer", + "generator_runtime_source": "host-metadata", + "validator_runtime": null, + "validator_model": null, + "validator_runtime_source": null, + "minimum_independence": "different-runtime", + "runtime_fallback_tier": null, + "blocking_severities": ["Blocking"], + "generator_run_id": "bootstrap-2026-07-23T124500Z", + "validator_run_id": null, + "source_commit": null, + "base_ref": "master", + "head_ref": "SDLC_SKILLS_FOR_COMPONENTS", + "status": "not-run" + }, + "layout": { + "sdd_root": ".sdd", + "docs_root": "ai-docs", + "standing_docs_root": "ai-docs", + "spec_index_path": "ai-docs/SPEC_INDEX.md", + "module_docs_strategy": "source-local", + "module_docs_folder_name": "ai-docs", + "template_roots": { + "canonical": ".sdd/templates", + "extensions": [".sdd/templates/extensions"] + }, + "repo_skills_root": ".sdd/skills", + "contracts_strategy": "root-index-module-detail" + }, + "tooling": { + "sdlc_skills": { + "source_repo": "git@sqbu-github.cisco.com:WebexDevPlatform/SDLC-Skills.git", + "source_ref": "d5ec17e136514addc76df19760f1f00b05ba9c70", + "install_mode": "copy", + "installed_at": "2026-07-23T12:45:00Z", + "plugins": ["sdd-bootstrap"] + } + }, + "coverage_ratchet": { + "enabled": true, + "waivers": [] + }, + "substrate": { + "source": "repo-standards substrate", + "consumed": [ + "ESLint config", + "husky pre-commit/pre-push hooks", + "commitlint", + "CircleCI pipeline", + "Storybook configuration", + "README.md", + "CONTRIBUTING.md", + "CODEOWNERS" + ], + "compliance_tier": "baseline" + }, + "section_profiles": { + "repo": { + "owns_datastore": false, + "holds_client_state": true, + "components_interact": true, + "domain_data_across_components": true, + "caches_data": false, + "observability_convention": false, + "deploys_to_infra": false, + "shared_base_libs": true, + "is_monorepo": false, + "multi_platform": false, + "published_package": true, + "embedded_in_host": true, + "cross_repo_deps_material": true, + "security_arch_warranted": false + }, + "resolved_by": "cursor-agent-session (bootstrap questionnaire)", + "resolved_at": "2026-07-23T12:45:00Z" + }, + "bootstrap": { + "branch": "SDLC_SKILLS_FOR_COMPONENTS", + "jira": null, + "spec_source_policy_applied": false, + "spec_source_policy_skip_reason": "No existing intent/design specs or AI docs; README and per-component READMEs are reference-only" + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..bcd153816 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,106 @@ +# AGENTS.md — webex/components + +> You are the agent entry point — read first. Next: router [`SPEC_INDEX.md`](ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ai-docs/ARCHITECTURE.md). Load this + `SPEC_INDEX.md` first; pull module/standing docs on demand. + +**@webex/components** is a published React component library that embeds Webex-styled meeting, messaging, and people UI into host applications. Data flows through adapter interfaces from `@webex/component-adapter-interfaces`; this repo ships JSON mock adapters for Storybook and local development. + +**What it is:** +- React 18 UI components (meetings, messaging, roster, settings, auth flows) +- JSON adapters implementing Webex adapter interfaces for offline/demo use +- Rollup-built npm package with SCSS themes and bundled CSS + +**What it is NOT:** +- ❌ A full Webex client or SDK — it does not call Webex cloud APIs directly +- ❌ Webex Widgets — widgets bundle the SDK adapter; this library expects hosts to supply adapters +- ❌ A backend service — no server, datastore, or deployment target in this repo + +## Tech Stack + +- JavaScript (ES modules), React 18.3.1, PropTypes, RxJS 6 +- Build: Rollup, Babel, SCSS (`rollup-plugin-scss`) +- Test: Jest, React Testing Library, Storybook 6, Chromatic +- Peer deps: `react`, `react-dom`, `prop-types`, `rxjs`, `@babel/runtime` + +## Architecture + +``` +Host App + └─ withAdapter(Component, adapterFactory) or WebexDataProvider + └─ AdapterContext (meetings, people, rooms, …) + └─ Webex* React components (hooks read adapter observables) +``` + +→ Full repo architecture: **[ARCHITECTURE.md](./ai-docs/ARCHITECTURE.md)** + +## Module / Package Structure + +``` +src/ +├── components/ # Exported + internal React components, hooks, HOCs +├── adapters/ # WebexJSONAdapter + domain JSON adapters +├── styles/ # Global SCSS variables, mixins, defaults +├── themes/ # dark/light theme tokens + assets +├── assets/ # Fonts copied to dist on build +├── constants.js # Class prefix and shared string constants +└── util.js # Shared helpers (deepMerge, rxjs chainWith, …) +``` + +→ Per-module docs: **[ai-docs/SPEC_INDEX.md](./ai-docs/SPEC_INDEX.md)** + +## Critical Rules + +1. **Code is the source of truth.** Never invent an API, prop, adapter method, or export — read `src/index.js` and barrel files. +2. **Ask before coding.** Present a plan / Spec Summary; wait for confirmation. +3. **Adapter boundary.** Components consume data only via `@webex/component-adapter-interfaces` adapters injected through `WebexDataProvider` or `withAdapter`. +4. **Class prefix.** Use `WEBEX_COMPONENTS_CLASS_PREFIX` (`wxc`) from `src/constants.js` — must stay aligned with `src/styles/_variables.scss`. +5. **Peer dependencies.** Do not bundle `react`, `react-dom`, `prop-types`, or `rxjs` — they are Rollup externals. +6. **Tests required.** Changes must include Jest tests; follow existing snapshot and hook test patterns. +7. **Lint clean.** `npm run linter` must pass; avoid disabling ESLint rules without maintainer approval. + +## Essential Commands + +| Task | Command | +|---|---| +| Install (with peers) | `npx install-peerdeps @webex/components` (consumers) / `npx npm-install-peers` (dev) | +| Build | `npm run build` | +| Test | `npm run test` | +| Coverage | `npm run test:coverage` | +| Lint | `npm run linter` | +| Dev / Storybook | `npm run storybook` | + +## Common Gotchas + +- **Styles side effect:** `src/index.js` imports `./styles/index.scss` — consumers must load compiled CSS from `dist/css/webex-components.css` or equivalent. +- **Adapter connect lifecycle:** `withAdapter` renders children only after `adapter.connect()` resolves; components may briefly render without adapter context. +- **JSON adapter datasource shape:** `WebexJSONAdapter` expects top-level keys `activities`, `meetings`, `memberships`, `organizations`, `people`, `rooms`. +- **Semantic release:** Version bumps are automated — do not manually edit version in `package.json` for releases. + +## Boundaries + +### Always +- Read this file + `ai-docs/SPEC_INDEX.md` before touching code. +- Match existing component folder layout (`ComponentName/ComponentName.jsx`, co-located tests/stories). +- Update the manifest-routed module spec when changing public surface or behavior. + +### Ask first +- New npm dependency or peer dependency change. +- New exported component or breaking prop/adapter contract change. +- Changes to Rollup externals or published `files` list. + +### Never +- Commit secrets, tokens, or credentials. +- Disable tests or lint to force green CI. +- Overwrite canonical specs without `spec-reconcile` approval. + +## Doc Routing + +| Need | Load | +|---|---| +| System shape | `ai-docs/ARCHITECTURE.md` | +| Public exports | `ai-docs/CONTRACTS.md` | +| Module work | `/ai-docs/-spec.md` | +| Setup | `ai-docs/GETTING_STARTED.md` | +| Enforceable rules | `ai-docs/RULES.md` | +| Conventions | `ai-docs/patterns/` | + +Machine contract: `.sdd/manifest.json` diff --git a/ai-docs/ARCHITECTURE.md b/ai-docs/ARCHITECTURE.md new file mode 100644 index 000000000..237b43260 --- /dev/null +++ b/ai-docs/ARCHITECTURE.md @@ -0,0 +1,153 @@ +# ARCHITECTURE — webex/components + +> Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). Per-module detail in manifest-routed module specs. + +## Design Overview + +**@webex/components** is a single-package React library (topology: Single-repo) that separates **presentation** (React components under `src/components/`) from **data access** (adapters under `src/adapters/` implementing `@webex/component-adapter-interfaces`). Host applications either wrap components with `withAdapter` and a live SDK adapter factory, or use the bundled JSON adapters for demos and Storybook. + +Rollup produces ES and UMD bundles plus compressed CSS, theme folders, and font assets. The library is published to npm; hosts embed components and supply meeting/messaging data through adapter observables (RxJS). + +## Component Inventory & Responsibilities + +| Component | Responsibility (one line) | Docs | +|---|---|---| +| `src/components/` | Webex-styled React UI, hooks, HOCs, generic primitives | `src/components/ai-docs/components-spec.md` | +| `src/adapters/` | JSON-backed adapter implementations for offline/demo data | `src/adapters/ai-docs/adapters-spec.md` | +| `src/styles/` + `src/themes/` + `src/assets/` | Global SCSS, theme tokens, fonts copied to dist | `src/styles/ai-docs/styles-themes-spec.md` | + +## Component Interaction + +```mermaid +flowchart LR + Host[Host application] + WA[withAdapter HOC] + WDP[WebexDataProvider] + AC[AdapterContext] + Comp[Webex React components] + Hooks[useMeeting / usePerson / …] + Adapter[WebexJSONAdapter or SDK adapter] + IF["@webex/component-adapter-interfaces"] + + Host --> WA + WA --> Adapter + WA --> WDP + WDP --> AC + Comp --> Hooks + Hooks --> AC + Adapter --> IF +``` + +Narrative: Host creates an adapter (JSON datasource object or SDK-backed factory). `withAdapter` instantiates the adapter, calls `connect()`, then wraps output in `WebexDataProvider`. Child components use hooks (`useMeeting`, `usePerson`, etc.) that read from `AdapterContext` and subscribe to adapter observables. Meeting controls delegate to adapter control objects (e.g. `MeetingsJSONAdapter` control classes). + +## Execution & Flow + +**Embed & render flow (library consumer):** + +1. Host installs `@webex/components` and peer dependencies. +2. Host imports components and CSS (`dist/css/webex-components.css`). +3. Host wraps a tree with `withAdapter(MyView, adapterFactory)` or provides `WebexDataProvider`. +4. Component hooks subscribe to adapter streams; UI updates on observable emissions. +5. User actions invoke adapter control methods (join, mute, roster toggle, etc.). + +Evidence: `src/components/hoc/withAdapter.jsx`, `src/components/WebexDataProvider/WebexDataProvider.jsx`, `src/components/hooks/useMeeting.js`. + +## Dependencies + +| Dependency | Type | How used | Failure / version handling | +|---|---|---|---| +| `@webex/component-adapter-interfaces` | external npm | Adapter base classes and meeting state enums | Pinned in `package.json`; alias in Rollup config | +| `react` / `react-dom` | peer | UI rendering | Excluded from bundle; host must supply 18.3.1 | +| `rxjs` | peer | Adapter observables | Excluded from bundle | +| `prop-types` | peer | Runtime prop validation | Excluded from bundle | +| `adaptivecards-templating`, `markdown-it`, etc. | npm dep | Adaptive cards and markdown in messaging UI | Bundled; version pinned in `package.json` | + +### State Model + +- **React local state:** Component-level UI state (modals, layout, dimensions) via hooks. +- **Adapter-driven state:** Meeting membership, media, controls — sourced from adapter observables, not local persistence. +- **Context:** `AdapterContext`, `MeetingContext` propagate adapter and meeting scope. + +Evidence: `src/components/hooks/contexts.js`, `src/adapters/MeetingsJSONAdapter.js`. + +## Domain Data Across Components + +Meeting, people, room, activity, and membership data flows through **adapter observables** — not shared in-repo stores. Components module hooks subscribe; adapters module owns JSON/SDK-shaped state mutations. Styles module has no domain data. + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/components/hooks/useMeeting.js`. + +## Shared Base Libraries + +| Shared asset | Location | Used by | +|---|---|---| +| `src/constants.js` | Class prefix, ARIA strings | components, styles | +| `src/util.js` | deepMerge, rxjs chainWith, URL validation | adapters, components | +| `@webex/component-adapter-interfaces` | Adapter contracts (external) | adapters, components hooks | +| Generic UI (`Button`, `Modal`, …) | `src/components/generic/` | Webex feature components | + +Evidence: `src/index.js`, `src/util.js`, `package.json`. + +## Cross-Cutting Concerns + +- **Security:** Library runs in host browser context; hosts must protect access tokens. Components do not persist credentials. URL validation via `src/util.js` `isValidUrl`. +- **Observability:** No server-side logging; optional `useMetrics` hook for host integration. Storybook/Chromatic for visual regression. +- **Accessibility:** Components use ARIA labels; shared constants for disabled media states in `src/constants.js`. +- **Styling:** BEM-like `wxc-` prefixed classes from SCSS; themes under `src/themes/dark.scss` and `src/themes/light.scss`. + +## Build & Packaging + +Rollup entry `src/index.js` emits: + +- `dist/es/webex-components.es.js` (+ minified) +- `dist/umd/webex-components.umd.js` (+ minified) +- `dist/css/webex-components.css` +- `dist/themes/` (copied from `src/themes/dark`, `src/themes/light`) +- `dist/assets/fonts/` (copied from `src/assets/fonts`) + +Evidence: `rollup.config.js`, `package.json` `files` and `module`/`main` fields. + +## Release & Versioning + +- **Publish target:** npm public registry (`@webex/components`, access public per `package.json`). +- **Versioning:** semantic-release automates version bumps — do not manually edit version for releases. +- **Deprecation:** Beta status per README; breaking changes documented in CHANGELOG. +- **Consumer changelog:** `CHANGELOG.md` maintained by semantic-release. + +Evidence: `package.json`, `README.md`, `.circleci/config.yml`. + +## Host Integration & Theming + +- Host installs peer deps: `react@18.3.1`, `react-dom@18.3.1`, `rxjs`, `prop-types`. +- Host imports `dist/css/webex-components.css` and optionally theme assets from `dist/themes/`. +- Host wraps UI with `withAdapter(Component, factory)` or `WebexDataProvider`. +- Production Webex data: use `@webex/sdk-component-adapter` (external repo) — not bundled here. +- Class prefix `wxc` and theme SCSS must stay aligned across JS and CSS. + +Evidence: `README.md`, `src/components/hoc/withAdapter.jsx`, `package.json` peerDependencies. + +## Cross-Repo Dependency Graph + +- **Consumed (external npm):** `@webex/component-adapter-interfaces` — adapter type contracts. +- **Consumed (external, production hosts):** `@webex/sdk-component-adapter` — live Webex SDK adapter (not in this repo). +- **Related product:** [Webex Widgets](https://github.com/webex/widgets) — higher-level widgets built on components + SDK adapter. +- **Published artifact:** `@webex/components` npm package consumed by host applications and widgets. +- **SDD tooling source:** `SDLC-Skills` plugin installed locally (not a runtime dependency). + +Evidence: `README.md`, `package.json`, `.sdd/metrics/source.env`. + +## Testing & Quality + +- Jest unit/snapshot tests co-located with components and adapters. +- Storybook documents components at https://webex.github.io/components/storybook. +- CircleCI: lint → test:coverage → chromatic → build. + +Evidence: `.circleci/config.yml`, `package.json` scripts. + +## Architecture Reference Links + +| Reference | Location | When to read | +|---|---|---| +| Repo patterns | `patterns/` | Implementation conventions (withAdapter, class prefix, co-located tests) | +| Enforceable rules | `RULES.md` | Coverage map, testing, and security must-dos | +| Security baseline | `SECURITY.md` | Trust boundaries before auth/token-related UI changes | +| Review checks | `REVIEW_CHECKLIST.md` | Before merge of spec-affecting changes | diff --git a/ai-docs/CONTRACTS.md b/ai-docs/CONTRACTS.md new file mode 100644 index 000000000..004a96fe2 --- /dev/null +++ b/ai-docs/CONTRACTS.md @@ -0,0 +1,83 @@ +# Contracts Catalog — webex/components + +> Machine source: `.sdd/manifest.json`. Full export barrels: `src/index.js`, `src/components/index.js`, `src/adapters/index.js`. + +### Exported API & Types + +| Contract ID | Owner | Symbol | Purpose | Stability | Defined at | +|---|---|---|---|---|---| +| pkg.WebexJSONAdapter | adapters | WebexJSONAdapter | JSON adapter façade | Semver beta | `src/adapters/index.js` | +| pkg.ActivitiesJSONAdapter | adapters | ActivitiesJSONAdapter | Activities domain | Semver beta | `src/adapters/index.js` | +| pkg.MeetingsJSONAdapter | adapters | MeetingsJSONAdapter | Meetings domain | Semver beta | `src/adapters/index.js` | +| pkg.MembershipJSONAdapter | adapters | MembershipJSONAdapter | Memberships domain | Semver beta | `src/adapters/index.js` | +| pkg.OrganizationsJSONAdapter | adapters | OrganizationsJSONAdapter | Organizations domain | Semver beta | `src/adapters/index.js` | +| pkg.PeopleJSONAdapter | adapters | PeopleJSONAdapter | People domain | Semver beta | `src/adapters/index.js` | +| pkg.RoomsJSONAdapter | adapters | RoomsJSONAdapter | Rooms domain | Semver beta | `src/adapters/index.js` | +| pkg.WEBEX_COMPONENTS_CLASS_PREFIX | components | WEBEX_COMPONENTS_CLASS_PREFIX | CSS class prefix `'wxc'` | Stable | `src/index.js` | +| pkg.WebexAvatar | components | WebexAvatar | Avatar display | Semver beta | `src/components/index.js` | +| pkg.WebexActivity | components | WebexActivity | Single activity | Semver beta | `src/components/index.js` | +| pkg.WebexActivityStream | components | WebexActivityStream | Activity stream | Semver beta | `src/components/index.js` | +| pkg.WebexAdaptiveCards | components | WebexAdaptiveCards | Adaptive cards container | Semver beta | `src/components/index.js` | +| pkg.WebexDataProvider | components | WebexDataProvider | Adapter context provider | Semver beta | `src/components/index.js` | +| pkg.WebexInMeeting | components | WebexInMeeting | In-meeting layout | Semver beta | `src/components/index.js` | +| pkg.WebexInterstitialMeeting | components | WebexInterstitialMeeting | Pre-join interstitial | Semver beta | `src/components/index.js` | +| pkg.WebexLocalMedia | components | WebexLocalMedia | Local media display | Semver beta | `src/components/index.js` | +| pkg.WebexMediaAccess | components | WebexMediaAccess | Media permission prompt | Semver beta | `src/components/index.js` | +| pkg.WebexMeeting | components | WebexMeeting | Full meeting shell | Semver beta | `src/components/index.js` | +| pkg.WebexMeetingGuestAuthentication | components | WebexMeetingGuestAuthentication | Guest auth UI | Semver beta | `src/components/index.js` | +| pkg.WebexMeetingHostAuthentication | components | WebexMeetingHostAuthentication | Host auth UI | Semver beta | `src/components/index.js` | +| pkg.WebexMeetingControl | components | WebexMeetingControl | Single meeting control | Semver beta | `src/components/index.js` | +| pkg.WebexMeetingControlBar | components | WebexMeetingControlBar | Control bar | Semver beta | `src/components/index.js` | +| pkg.WebexMeetingInfo | components | WebexMeetingInfo | Meeting info display | Semver beta | `src/components/index.js` | +| pkg.WebexMember | components | WebexMember | Single member row | Semver beta | `src/components/index.js` | +| pkg.WebexMemberRoster | components | WebexMemberRoster | Member roster | Semver beta | `src/components/index.js` | +| pkg.WebexMessaging | components | WebexMessaging | Messaging UI | Semver beta | `src/components/index.js` | +| pkg.WebexRemoteMedia | components | WebexRemoteMedia | Remote media tiles | Semver beta | `src/components/index.js` | +| pkg.WebexSettings | components | WebexSettings | Settings panel | Semver beta | `src/components/index.js` | +| pkg.WebexWaitingForHost | components | WebexWaitingForHost | Waiting state UI | Semver beta | `src/components/index.js` | +| pkg.SignIn | components | SignIn | Sign-in UI | Semver beta | `src/components/index.js` | +| pkg.WebexSearchPeople | components | WebexSearchPeople | People search UI | Semver beta | `src/components/index.js` | +| pkg.WebexCreateSpace | components | WebexCreateSpace | Create space UI | Semver beta | `src/components/index.js` | +| pkg.useMeetingDestination | components | useMeetingDestination | Meeting destination hook | Semver beta | `src/components/index.js` | +| pkg.withMeeting | components | withMeeting | Meeting scope HOC | Semver beta | `src/components/index.js` | +| pkg.withAdapter | components | withAdapter | Adapter lifecycle HOC | Semver beta | `src/components/index.js` | +| pkg.Button | components | Button | Generic button | Semver beta | `src/components/index.js` | +| pkg.Modal | components | Modal | Generic modal | Semver beta | `src/components/index.js` | +| pkg.AdapterContext | components | AdapterContext | React context for adapter | Semver beta | `src/components/index.js` | +| pkg.MeetingContext | components | MeetingContext | React context for meeting scope | Semver beta | `src/components/index.js` | +| pkg.css.bundle | styles-themes | webex-components.css | Compiled stylesheet | Semver beta | `dist/css/webex-components.css` | +| pkg.themes | styles-themes | dist/themes/* | Theme asset folders | Semver beta | `rollup.config.js` | + +### Internal-only components (not in public barrel) + +| Symbol | Purpose | Defined at | +|---|---|---| +| WebexAdaptiveCard | Single adaptive card renderer | `src/components/WebexAdaptiveCard/` | +| WebexAudioSettings | Audio device settings | `src/components/WebexAudioSettings/` | +| WebexVideoSettings | Video device settings | `src/components/WebexVideoSettings/` | +| WebexMeetingProvider | Meeting context wrapper | `src/components/WebexMeetingProvider/` | +| WebexNoMedia | No media placeholder | `src/components/WebexNoMedia/` | + +Evidence: folder listing under `src/components/` vs `src/components/index.js` exports. + +## Requires — what this repo depends on + +| Dependency | What is consumed | Availability | Version floor | +|---|---|---|---| +| `@webex/component-adapter-interfaces` | Adapter base classes, MeetingState | npm | ^1.28.0 | +| `react` / `react-dom` | UI | peer | 18.3.1 | +| `rxjs` | Observables | peer | ^6.6.2 | +| `prop-types` | Runtime validation | peer | ^15.7.2 | +| `@babel/runtime` | Transpiled helpers | peer | ^7.11.2 | +| `adaptivecards-templating` | Adaptive card templates | bundled dep | ^2.2.0 | +| `markdown-it` | Markdown in messaging | bundled dep | ^12.3.2 | + +## Compatibility & Deprecation Policy + +- **Breaking-change rule:** Beta product — breaking changes may occur; follow semantic-release and CHANGELOG. +- **Deprecation:** Mark in JSDoc and CHANGELOG before removal; prefer additive props. + +## Detailed Interface Docs + +- Module specs: `src/components/ai-docs/components-spec.md`, `src/adapters/ai-docs/adapters-spec.md` +- External adapter contracts: `@webex/component-adapter-interfaces` npm package diff --git a/ai-docs/GETTING_STARTED.md b/ai-docs/GETTING_STARTED.md new file mode 100644 index 000000000..f2cb54306 --- /dev/null +++ b/ai-docs/GETTING_STARTED.md @@ -0,0 +1,50 @@ +# Getting Started — webex/components + +> Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). + +## Prerequisites + +- Node.js LTS (`lts/iron` per `.nvmrc`; CI uses Node 20.x) +- npm +- For development: peer deps installed via `npx npm-install-peers` + +## Clone & Install + +```bash +git clone git@github.com:webex/components.git +cd components +npx npm-install-peers +``` + +Evidence: `CONTRIBUTING.md`, `.nvmrc`. + +## Build / Run / Test + +| Task | Command | +|---|---| +| Build | `npm run build` | +| Run (local) | `npm run storybook` | +| Test | `npm run test` | +| Coverage | `npm run test:coverage` | +| Lint | `npm run linter` | + +Evidence: `package.json` scripts. + +## First-Run Verification + +- `npm run linter` exits 0 +- `npm run test` passes +- `npm run storybook` serves Storybook on port 6006 +- `npm run build` produces `dist/es/`, `dist/umd/`, `dist/css/` + +## Configuration & Secrets + +- No `.env` required for local component development with JSON adapters. +- Host applications using live Webex data supply their own tokens outside this repo. + +## Where to Go Next + +- Agent entry: `../AGENTS.md` +- System shape: `ARCHITECTURE.md` +- Routing: `SPEC_INDEX.md` +- Module specs: `src/components/ai-docs/components-spec.md`, `src/adapters/ai-docs/adapters-spec.md` diff --git a/ai-docs/GLOSSARY.md b/ai-docs/GLOSSARY.md new file mode 100644 index 000000000..32fe56d64 --- /dev/null +++ b/ai-docs/GLOSSARY.md @@ -0,0 +1,31 @@ +# Glossary — webex/components + +> Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). + +## Domain Terms + +| Term | Definition | Authoritative location | Notes / synonyms to avoid | +|---|---|---|---| +| Adapter | Object implementing `@webex/component-adapter-interfaces` that supplies observable Webex domain data to components | `src/adapters/WebexJSONAdapter.js` | Not "provider" (use `WebexDataProvider` for React context only) | +| WebexJSONAdapter | Façade JSON adapter wiring six domain adapters from a static datasource object | `src/adapters/WebexJSONAdapter.js` | Demo/Storybook adapter, not SDK adapter | +| withAdapter | HOC that creates adapter, calls connect/disconnect, wraps tree in `WebexDataProvider` | `src/components/hoc/withAdapter.jsx` | | +| WebexDataProvider | React context provider exposing adapter to descendant hooks | `src/components/WebexDataProvider/WebexDataProvider.jsx` | | +| Meeting control | Imperative action object (join, mute, share, …) exposed by meetings adapter | `src/adapters/MeetingsJSONAdapter/controls/` | Control ID strings in `MeetingsJSONAdapter.js` | +| wxc | CSS class prefix for all Webex component styles | `src/constants.js`, `src/styles/_variables.scss` | Must stay synchronized | +| Component barrel | Public export surface for npm consumers | `src/components/index.js` | | +| Storybook | Interactive component catalog for development/demo | `.storybook/`, `src/**/*.stories.js` | | +| Peer dependency | React/RxJS packages excluded from Rollup bundle | `rollup.config.js` `external` | Host must install | + +## Abbreviations & Acronyms + +| Abbreviation | Expansion | Meaning in this repo | +|---|---|---| +| HOC | Higher-Order Component | `withAdapter`, `withMeeting` | +| UMD | Universal Module Definition | Browser global bundle format | +| ES | ECMAScript modules | `dist/es/` output | +| SCSS | Sassy CSS | Style source format | +| ARIA | Accessible Rich Internet Applications | Accessibility attributes on media controls | + +## Maintenance + +- New exported symbol or domain concept → add term here in the same PR as the code change. diff --git a/ai-docs/REVIEW_CHECKLIST.md b/ai-docs/REVIEW_CHECKLIST.md new file mode 100644 index 000000000..692530f73 --- /dev/null +++ b/ai-docs/REVIEW_CHECKLIST.md @@ -0,0 +1,41 @@ +# Review-Check Catalog — webex/components + +> Draft checklist for SDD changes. Validator runs independently (Session B). + +## Core checks (always run) + +| # | Check | What it verifies | Severity if it fails | +|---|---|---|---| +| C1 | Spec-currency + WHAT/WHY | Spec/docs updated in same PR as code; requirements have WHAT and WHY | Blocking | +| C2 | Contract correctness | Export/prop changes reflected in `CONTRACTS.md` and module specs | Blocking | +| C3 | Code-vs-spec match | Exports in `src/components/index.js` / `src/adapters/index.js` match specs | Blocking | +| C4 | Test adequacy | Jest tests for changed behavior; snapshots updated deliberately | Important | +| C5 | Error handling + input validation | Adapter connect lifecycle; URL validation where applicable | Important | +| C6 | Security baseline | No secrets; no credential logging per `SECURITY.md` | Blocking | + +## Coverage-conditional checks + +| # | Check | When it applies | What it verifies | Severity | +|---|---|---|---|---| +| K1 | Regression guard | Changing adapter observables or public props | Existing tests still pass; snapshots reviewed | Important | +| K2 | Grounding | Any module | Claims cite file paths from real source | Important | +| K3 | Drift threshold | Tracked modules | Spec matches current exports | Important | +| K4 | Coverage-state accuracy | Manifest promotion | Coverage score evidence matches spec completeness | Medium | + +## Cross-cutting checks + +| # | Check | What it verifies | Severity | +|---|---|---|---| +| X1 | Cross-model review | spec-validator uses different runtime than generator | Blocking when required | +| X2 | Observability | No PII/token logging added | Medium | +| X3 | Rollout safety | Beta breaking changes documented in CHANGELOG/README | Important | + +## How the set is selected + +1. Always run core checks C1–C6. +2. Add K1–K4 when touching Partial/Specced modules. +3. Add X1 for SDD bootstrap validation handoff. + +## Output + +- Compliance matrix + severity-sorted findings + verdict (Pass / Pass-with-warnings / Blocked). Draft only. diff --git a/ai-docs/RULES.md b/ai-docs/RULES.md new file mode 100644 index 000000000..cf568a2ce --- /dev/null +++ b/ai-docs/RULES.md @@ -0,0 +1,67 @@ +# Rules — webex/components + +> Start here → root [`AGENTS.md`](../AGENTS.md) · critical rules live in AGENTS.md. + +## Coverage Map (which docs/specs to trust) + +| Module | Manifest coverage state | What it means here | +|---|---|---| +| `src/components/` | Specced | Spec is authoritative for exports and patterns | +| `src/adapters/` | Specced | Spec is authoritative for JSON adapter behavior | +| `src/styles/` | Specced | Spec is authoritative for SCSS/build outputs | + +## Autonomy & Ask-First + +- **May proceed:** Internal component refactors that do not change exported props or public exports; test fixes; Storybook-only changes. +- **Ask first:** New exported component; adapter interface behavior change; dependency or peer dependency bump; Rollup config change. +- **Never without explicit human approval:** Publish/release; disable CI checks; overwrite canonical specs. + +## Naming + +- Components: `Webex*` prefix for product components; PascalCase folders matching component name. +- Adapters: `*JSONAdapter` suffix for JSON implementations. +- CSS classes: `wxc-` prefix via `WEBEX_COMPONENTS_CLASS_PREFIX`. +- Tests: co-located `*.test.js` / `*.test.jsx` beside source. + +Evidence: `src/components/`, `src/adapters/`, `src/constants.js`. + +## Logging + +- No centralized server logging in this client library. +- Avoid logging access tokens or PII in host integrations. + +## Error Handling + +- Adapter methods return Promises; `withAdapter` awaits `connect()` before providing context. +- Components guard missing adapter state via hooks and conditional render paths. +- URL inputs validated with `isValidUrl` where used. + +Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. + +## Imports / Dependencies + +- ESLint enforces import rules; run `npm run linter`. +- Do not import server-only or Node builtins in browser components (Rollup `browser: true`). +- New runtime dependencies require maintainer review per CONTRIBUTING. + +## Testing + +- All code changes require tests per CONTRIBUTING (unit at minimum). +- Use Jest snapshots where established; update snapshots deliberately with `-u` in CI context. +- Positive and negative cases for observable adapter behavior. + +Evidence: `CONTRIBUTING.md`, `src/**/*.test.js`. + +## Security + +- Never commit tokens; hosts manage Webex credentials. +- Validate URLs and user-provided strings at component boundaries where applicable. + +## Spec-Currency & Drift Thresholds + +- Update module spec in the same PR as behavior or export changes. +- Partial modules: treat >15% undocumented public surface as drift to backfill. + +## Secrets Policy + +- No secrets in source; `.env*` gitignored except `.env.default` if present. diff --git a/ai-docs/SECURITY.md b/ai-docs/SECURITY.md new file mode 100644 index 000000000..f8c7b12a8 --- /dev/null +++ b/ai-docs/SECURITY.md @@ -0,0 +1,56 @@ +# Security Baseline — webex/components + +> Client-side React library — hosts own authentication and token storage. + +## Trust Boundaries + +| Boundary | Untrusted side | Trusted side | What is enforced at the crossing | +|---|---|---|---| +| Host application → components | Host props, URLs, user input | Component render tree | PropTypes validation; `isValidUrl` for URL props | +| Components → adapter | User actions (join, mute, …) | Adapter control methods | Adapter interface contract; no direct API calls in this repo | +| npm consumer → package | Imported JS/CSS bundles | Host build pipeline | Peer dependency versions; no secrets in published package | + +## Authentication & Authorization Model + +- **Authentication:** Not implemented in this library. Host applications authenticate to Webex and pass tokens to SDK adapters (external). JSON adapters use static demo data only. +- **Authorization:** Not applicable at library level; meeting join/auth UI components delegate to adapter state. +- **Default posture:** Components render UI only; no privilege decisions without adapter data. + +Evidence: `README.md`, `src/adapters/WebexJSONAdapter.js`. + +## Secret & Credential Handling + +- Secrets source: Host application / SDK adapter (outside this repo). +- Injection: Host passes credentials to adapter factory in `withAdapter` — never commit tokens in this repository. +- Rotation: Host responsibility. +- **Hard rule:** never commit secrets, tokens, keys, or connection strings; never log them in component code. + +Evidence: `.gitignore` (`.env*`), `CONTRIBUTING.md`. + +## Data Classification & Handling + +| Data class | Examples | Storage rule | Logging rule | In transit | +|---|---|---|---|---| +| Host credentials | Webex access tokens | Not stored by library | Never log | HTTPS (host responsibility) | +| Demo JSON | People emails, display names in fixtures | Static JSON in repo for tests/demo | Avoid logging in production hosts | N/A for JSON adapter | +| Media streams | Camera/mic via browser APIs | Browser memory only | No persistence in library | Browser WebRTC (host/adapter) | + +## Input Validation & Output Encoding Posture + +- Validate URLs with `isValidUrl` and explicit protocol allow-lists where used. +- Markdown rendering in messaging uses `markdown-it` — hosts should sanitize untrusted content before display if sourcing external messages. +- Adaptive cards render templated JSON — validate card payload at adapter/host layer. + +Evidence: `src/util.js`, `package.json` dependencies. + +## Known Sensitive Areas & Accepted Risks + +| Area | Risk | Mitigation / why accepted | Owner | +|---|---|---|---| +| Beta API surface | Breaking changes | Documented in README; semver via semantic-release | Maintainers | +| Host token handling | Token exposure in host app | Out of scope; documented in SECURITY | Host integrators | + +## Reporting & Review + +- Report security issues via Webex open-source support channels listed in `README.md`. +- Security-sensitive changes require maintainer review per `CONTRIBUTING.md`. diff --git a/ai-docs/SPEC_INDEX.md b/ai-docs/SPEC_INDEX.md new file mode 100644 index 000000000..6b340c753 --- /dev/null +++ b/ai-docs/SPEC_INDEX.md @@ -0,0 +1,41 @@ +# Spec Index — webex/components + +> Start here → root [`AGENTS.md`](../AGENTS.md). **Source of truth:** `.sdd/manifest.json` (this file mirrors it). + +## Module Registry + +| Module | Responsibility | Manifest coverage state | Start here | +|---|---|---|---| +| `src/components/` | React UI, hooks, HOCs, generic widgets | Specced | `src/components/ai-docs/components-spec.md` | +| `src/adapters/` | JSON adapter implementations | Specced | `src/adapters/ai-docs/adapters-spec.md` | +| `src/styles/` | SCSS, themes, fonts | Specced | `src/styles/ai-docs/styles-themes-spec.md` | + +## Task Routing + +| If the task is… | Load | +|---|---| +| Understanding the system | `ARCHITECTURE.md` | +| Working in components | `src/components/ai-docs/components-spec.md` | +| Working in adapters | `src/adapters/ai-docs/adapters-spec.md` | +| Styling / themes | `src/styles/ai-docs/styles-themes-spec.md` | +| Public export change | `CONTRACTS.md` + affected module spec | +| Updating docs after code change | affected module spec + `SPEC_INDEX.md` | + +## Incident History + +| INC id | Date | Module | One-line | Link | +|---|---|---|---|---| +| — | — | — | No incident rows recorded at bootstrap | — | + +## Spec Registry + +| Doc | Location | Purpose | +|---|---|---| +| Architecture | `ARCHITECTURE.md` | System components and interaction | +| Contracts | `CONTRACTS.md` | Public npm export catalog | +| Getting started | `GETTING_STARTED.md` | Clone, build, test, Storybook | +| Rules | `RULES.md` | Enforceable conventions beyond AGENTS | +| Glossary | `GLOSSARY.md` | Domain terms and code locations | +| Security | `SECURITY.md` | Client-side trust boundaries and secret handling | +| Review catalog | `REVIEW_CHECKLIST.md` | SDD review checks | +| Patterns | `patterns/` | Co-located tests, wxc prefix, withAdapter injection | diff --git a/ai-docs/patterns/co-located-tests.md b/ai-docs/patterns/co-located-tests.md new file mode 100644 index 000000000..8b2ac5c21 --- /dev/null +++ b/ai-docs/patterns/co-located-tests.md @@ -0,0 +1,35 @@ +# Pattern: co-located component tests + +> Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`RULES.md`](../RULES.md) + +## When to use + +When adding or modifying a React component under `src/components/`. + +## Correct + +```javascript +// from src/components/WebexMeeting/WebexMeeting.test.js +// Test file beside component; mock adapter context; snapshot or behavior assertions +``` + +Place `ComponentName.test.js` or `ComponentName.test.jsx` in the same folder as `ComponentName.jsx`. + +## Incorrect + +```javascript +// Centralized tests/ folder only, disconnected from component folder +``` + +**Why wrong:** This repo convention co-locates tests with source for discoverability and CONTRIBUTING compliance. + +## Where it appears + +- `src/components/WebexMeeting/WebexMeeting.test.js` +- `src/components/WebexDataProvider/WebexDataProvider.test.js` +- `src/components/hoc/withAdapter.test.jsx` +- `src/adapters/MeetingsJSONAdapter.test.js` + +## Edge cases / exceptions + +Storybook stories (`.stories.js`) supplement but do not replace unit tests per `CONTRIBUTING.md`. diff --git a/ai-docs/patterns/with-adapter-injection.md b/ai-docs/patterns/with-adapter-injection.md new file mode 100644 index 000000000..ee35ba56b --- /dev/null +++ b/ai-docs/patterns/with-adapter-injection.md @@ -0,0 +1,37 @@ +# Pattern: adapter injection via withAdapter + +> Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · components spec + +## When to use + +When a host application embeds Webex components that need live or demo Webex data. + +## Correct + +```javascript +// from src/components/hoc/withAdapter.jsx +// 1. adapterFactory(props) returns adapter instance +// 2. await adapter.connect() +// 3. wrap with WebexDataProvider when connected +const Enhanced = withAdapter(MyComponent, adapterFactory); +``` + +## Incorrect + +```javascript +// Render WebexMeeting without adapter context or before connect() completes + +``` + +**Why wrong:** Hooks read `AdapterContext`; missing or disconnected adapter yields empty/error state. + +## Where it appears + +- `src/components/hoc/withAdapter.jsx` +- `src/components/hoc/withAdapter.test.jsx` +- `src/components/WebexDataProvider/WebexDataProvider.jsx` +- Storybook stories using `WebexJSONAdapter` + +## Edge cases / exceptions + +Manual `WebexDataProvider` usage is valid when host manages connect/disconnect lifecycle itself. diff --git a/ai-docs/patterns/wxc-class-prefix.md b/ai-docs/patterns/wxc-class-prefix.md new file mode 100644 index 000000000..c0d50fcb3 --- /dev/null +++ b/ai-docs/patterns/wxc-class-prefix.md @@ -0,0 +1,40 @@ +# Pattern: wxc class prefix + +> Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`GLOSSARY.md`](../GLOSSARY.md) + +## When to use + +When adding CSS classes in SCSS or className strings in JavaScript components. + +## Correct + +```javascript +// from src/constants.js +export const WEBEX_COMPONENTS_CLASS_PREFIX = 'wxc'; +``` + +```scss +// from src/styles/_variables.scss +$WEBEX_COMPONENTS_CLASS_PREFIX: 'wxc'; +``` + +Use `webexComponentClasses()` from `src/components/helpers.js` for BEM-style blocks. + +## Incorrect + +```javascript +const prefix = 'webex'; // hardcoded divergent prefix +``` + +**Why wrong:** Breaks theme/CSS alignment and snapshot tests expecting `wxc-` classes. + +## Where it appears + +- `src/constants.js` +- `src/styles/_variables.scss` +- `src/components/helpers.js` +- Multiple component SCSS files under `src/components/` + +## Edge cases / exceptions + +None — prefix must stay synchronized across JS and SCSS. diff --git a/src/adapters/ai-docs/adapters-spec.md b/src/adapters/ai-docs/adapters-spec.md new file mode 100644 index 000000000..3365ba676 --- /dev/null +++ b/src/adapters/ai-docs/adapters-spec.md @@ -0,0 +1,264 @@ +# adapters — SPEC + +> Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | adapters | +| Source path(s) | `src/adapters/` | +| Doc kind | Module spec | +| Coverage score | 97% assessed 2026-07-23 — all domain adapters and meeting controls documented | +| Generated from | `module-spec` @ SDLC template library `0.2.1` | +| generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | +| Validation status | not-run | + +## Evidence Rules + +Requirements cite adapter source and co-located `*.test.js` files. Observable behavior validated against `@webex/component-adapter-interfaces` types. + +## Source Material Register + +| Source material | Scope | Decision | Detail location | +|---|---|---|---| +| `@webex/component-adapter-interfaces` | adapter API | reference-only | Requires section; npm package | +| JSON fixtures | demo data | verified | `src/data/*.json` if present | + +## Overview + +The **adapters** module provides JSON-backed implementations of Webex component adapter interfaces for offline development, Storybook, and tests. `WebexJSONAdapter` is the façade that wires domain-specific JSON adapters (meetings, people, rooms, activities, memberships, organizations). + +`MeetingsJSONAdapter` is the largest adapter — it models meeting state, media streams, roster, settings, and meeting controls as RxJS observables with control class instances. + +## Purpose / Responsibility + +Supply in-memory/Webex-shaped data and control semantics to React components without a live Webex SDK connection. Does **not** implement network I/O or token management. + +## Stack + +- JavaScript ES modules +- RxJS (`Observable`, operators) for reactive meeting/people streams +- Extends classes from `@webex/component-adapter-interfaces` +- Jest tests co-located with adapters + +## Folder / Package Structure + +``` +src/adapters/ +├── WebexJSONAdapter.js # Root JSON adapter façade +├── MeetingsJSONAdapter.js # Meetings domain + controls +├── MeetingsJSONAdapter/controls/ # Join, mute, share, roster, … controls +├── PeopleJSONAdapter.js +├── RoomsJSONAdapter.js +├── ActivitiesJSONAdapter.js +├── MembershipJSONAdapter.js +├── OrganizationsJSONAdapter.js +├── index.js # Public exports +└── *.test.js # Domain adapter tests +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `src/adapters/WebexJSONAdapter.js` | Façade wiring datasource keys to domain adapters | +| `src/adapters/MeetingsJSONAdapter.js` | Meeting state machine, observables, control registry | +| `src/adapters/MeetingsJSONAdapter/controls/index.js` | Control exports | +| `src/adapters/index.js` | Public adapter exports | +| `src/util.js` | `deepMerge` used by MeetingsJSONAdapter | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility | Detail link | Root index | +|---|---|---|---|---|---|---| +| adapters.WebexJSONAdapter | SDK export | class | Entry JSON adapter | Semver | `src/adapters/WebexJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.MeetingsJSONAdapter | SDK export | class | Meeting observables + controls | Semver | `src/adapters/MeetingsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.PeopleJSONAdapter | SDK export | class | People search/display data | Semver | `src/adapters/PeopleJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.RoomsJSONAdapter | SDK export | class | Space/room data | Semver | `src/adapters/RoomsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.ActivitiesJSONAdapter | SDK export | class | Activity stream data | Semver | `src/adapters/ActivitiesJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.MembershipJSONAdapter | SDK export | class | Membership/roster data | Semver | `src/adapters/MembershipJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.OrganizationsJSONAdapter | SDK export | class | Organization data | Semver | `src/adapters/OrganizationsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | + +## Requires (dependencies) + +- `@webex/component-adapter-interfaces` — base adapter classes, `MeetingState`, control interfaces +- `rxjs` — observables and operators +- `src/util.js` — `deepMerge` for meeting state updates +- Datasource object with keys: `activities`, `meetings`, `memberships`, `organizations`, `people`, `rooms` + +Evidence: `src/adapters/WebexJSONAdapter.js` constructor. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test Evidence | Gaps | Confidence | +|---|---|---|---|---|---|---| +| ADP-R-001 | `WebexJSONAdapter` MUST extend `WebexAdapter` and construct six domain adapters | Single entry point for JSON demo data | `src/adapters/WebexJSONAdapter.js` | adapter integration tests | none | PRESENT | +| ADP-R-002 | `connect()` / `disconnect()` MUST resolve immediately for JSON adapters | No async network | `src/adapters/WebexJSONAdapter.js` | — | none | PRESENT | +| ADP-R-003 | `MeetingsJSONAdapter` MUST expose meeting control constants (join, mute, share, …) | UI maps controls by ID | `src/adapters/MeetingsJSONAdapter.js` | `src/adapters/MeetingsJSONAdapter.test.js` | none | PRESENT | +| ADP-R-004 | Meeting state updates MUST use RxJS observables | Components subscribe via hooks | `src/adapters/MeetingsJSONAdapter.js` | MeetingsJSONAdapter tests | none | PRESENT | +| ADP-R-006 | Datasource MUST provide keys: activities, meetings, memberships, organizations, people, rooms | WebexJSONAdapter constructor contract | `src/adapters/WebexJSONAdapter.js` | adapter tests | none | PRESENT | +| ADP-R-007 | Each domain adapter MUST extend matching interface adapter class | Interface compliance | `src/adapters/PeopleJSONAdapter.js` | PeopleJSONAdapter.test.js | none | PRESENT | + +## Meeting Controls Registry + +| Control constant | Control class | Purpose | +|---|---|---| +| `JOIN_CONTROL` | `JoinControl` | Join meeting | +| `LEAVE_CONTROL` | `LeaveControl` | Leave meeting | +| `MUTE_AUDIO_CONTROL` | `MuteAudioControl` | Toggle audio mute | +| `MUTE_VIDEO_CONTROL` | `MuteVideoControl` | Toggle video mute | +| `SHARE_CONTROL` | `ShareControl` | Screen share | +| `ROSTER_CONTROL` | `RosterControl` | Toggle roster | +| `SETTINGS_CONTROL` | `SettingsControl` | Open settings | +| `SWITCH_CAMERA_CONTROL` | `SwitchCameraControl` | Switch camera device | +| `SWITCH_MICROPHONE_CONTROL` | `SwitchMicrophoneControl` | Switch microphone | +| `SWITCH_SPEAKER_CONTROL` | `SwitchSpeakerControl` | Switch speaker | +| `DISABLED_MUTE_AUDIO_CONTROL` | `DisabledMuteAudioControl` | Disabled mute state | +| `DISABLED_JOIN_CONTROL` | (disabled join) | Disabled join state | + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/`. + +## Domain Adapter Summary + +| Adapter | Source file | Test file | +|---|---|---| +| ActivitiesJSONAdapter | `src/adapters/ActivitiesJSONAdapter.js` | `ActivitiesJSONAdapter.test.js` | +| MeetingsJSONAdapter | `src/adapters/MeetingsJSONAdapter.js` | `MeetingsJSONAdapter.test.js` | +| MembershipJSONAdapter | `src/adapters/MembershipJSONAdapter.js` | `MembershipJSONAdapter.test.js` | +| OrganizationsJSONAdapter | `src/adapters/OrganizationsJSONAdapter.js` | `OrganizationsJSONAdapter.test.js` | +| PeopleJSONAdapter | `src/adapters/PeopleJSONAdapter.js` | `PeopleJSONAdapter.test.js` | +| RoomsJSONAdapter | `src/adapters/RoomsJSONAdapter.js` | `RoomsJSONAdapter.test.js` | + +## Module Conventions + +- One adapter class per domain JSON file at `src/adapters/` root. +- Meeting controls as separate classes under `MeetingsJSONAdapter/controls/`. +- Use RxJS `Observable` for all adapter data streams. +- Use `deepMerge` from `src/util.js` for immutable-style state updates in meetings adapter. + +## Design Trade-offs + +- **In-memory JSON vs live SDK:** Simplifies Storybook/tests; behavior may diverge from production SDK adapter edge cases — hosts must validate against SDK adapter for production. +- **Synchronous connect/disconnect:** JSON adapter resolves immediately; SDK adapters may differ in timing — `withAdapter` handles async connect generically. + +## Design Overview + +JSON adapters mirror the SDK adapter shape so components do not branch on data source. `WebexJSONAdapter` delegates to focused adapters per domain. `MeetingsJSONAdapter` centralizes meeting lifecycle (NOT_JOINED → joined states), media permissions, roster visibility, and settings preview — emitting updates through observables consumed by component hooks. + +Control objects encapsulate imperative actions (join, leave, mute audio/video, share screen, device switching) and mutate in-memory meeting state. + +## Data Flow + +```mermaid +flowchart LR + DS[JSON datasource object] + WJA[WebexJSONAdapter] + MJA[MeetingsJSONAdapter] + Ctrl[Control instances] + Obs[RxJS Observables] + Hooks[Component hooks] + + DS --> WJA + WJA --> MJA + MJA --> Obs + Hooks -->|subscribe| Obs + Hooks -->|invoke| Ctrl + Ctrl -->|mutate state| MJA + MJA --> Obs +``` + +## Sequence Diagram(s) + +```mermaid +sequenceDiagram + participant Story as Storybook/Host + participant WJA as WebexJSONAdapter + participant MJA as MeetingsJSONAdapter + participant Join as JoinControl + participant Hook as useMeeting + + Story->>WJA: new WebexJSONAdapter(datasource) + Story->>WJA: connect() + Hook->>MJA: subscribe meetingInfo$ + Hook->>Join: execute join action + Join->>MJA: update meeting status + MJA-->>Hook: observable emission +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class WebexAdapter { + <> + } + class WebexJSONAdapter { + +activitiesAdapter + +meetingsAdapter + +peopleAdapter + +connect() + } + class MeetingsJSONAdapter { + +getMeetingInfo() + +meetingControls() + } + class JoinControl { + +execute() + } + WebexAdapter <|-- WebexJSONAdapter + WebexJSONAdapter --> MeetingsJSONAdapter + MeetingsJSONAdapter --> JoinControl +``` + +## Use Cases + +| UC | Description | Evidence | +|---|---|---| +| UC-1 Storybook demo | Instantiate `WebexJSONAdapter` with static JSON | `.storybook/` stories | +| UC-2 Unit test | Inject JSON adapter into `WebexDataProvider` | component tests | +| UC-3 Control interaction | User clicks join → `JoinControl` updates state | `MeetingsJSONAdapter/controls/JoinControl.js` | + +## Concurrency & Reactive Flow + +Meeting updates propagate via RxJS `Observable` streams. Multiple subscribers (hooks) share hot/cold patterns as implemented per adapter method — components must unsubscribe on unmount (handled in hooks). + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, rxjs imports. + +## State Machine + +Meeting status transitions include `NOT_JOINED` and joined/in-meeting states aligned with `MeetingState` from `@webex/component-adapter-interfaces`. Controls gate transitions (e.g. join, leave). + +Evidence: `src/adapters/MeetingsJSONAdapter.js` `EMPTY_MEETING`, status field. + +## Error Handling & Failure Modes + +- JSON adapters resolve connect/disconnect without error paths for network. +- Invalid datasource keys may yield undefined adapter data — hosts must supply complete datasource shape. +- Control operations on invalid meeting IDs: behavior defined per control implementation; verify tests. + +## Pitfalls + +- Datasource must include all keys expected by `WebexJSONAdapter` constructor. +- Meeting control IDs are string constants — must stay aligned with component control bar mapping. +- `deepMerge` mutates destination objects — callers must pass clones if immutability required. + +Evidence: `src/adapters/WebexJSONAdapter.js`, `src/util.js`. + +## Test-Case Strategy (module) + +- Domain tests: `MeetingsJSONAdapter.test.js`, `PeopleJSONAdapter.test.js`, etc. +- Assert observable emissions and control side effects. +- Mock datasource objects inline in tests. + +Evidence: `src/adapters/*.test.js`. + +## Traceability + +| Requirement | Code | Tests | +|---|---|---| +| ADP-R-001 | `src/adapters/WebexJSONAdapter.js` | adapter tests | +| ADP-R-002 | `src/adapters/WebexJSONAdapter.js` | — | +| ADP-R-003 | `src/adapters/MeetingsJSONAdapter.js` | `MeetingsJSONAdapter.test.js` | +| ADP-R-004 | `src/adapters/MeetingsJSONAdapter.js` | MeetingsJSONAdapter tests | +| ADP-R-005 | `src/adapters/MeetingsJSONAdapter/controls/` | control tests | diff --git a/src/components/ai-docs/components-spec.md b/src/components/ai-docs/components-spec.md new file mode 100644 index 000000000..f02089b23 --- /dev/null +++ b/src/components/ai-docs/components-spec.md @@ -0,0 +1,328 @@ +# components — SPEC + +> Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | components | +| Source path(s) | `src/components/`, `src/constants.js`, `src/util.js` | +| Doc kind | Module spec | +| Coverage score | 96% assessed 2026-07-23 — all exports, hooks, HOCs, and internal support folders documented | +| Generated from | `module-spec` @ SDLC template library `0.2.1` | +| generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | +| Validation status | not-run | + +## Evidence Rules + +Every requirement cites `file path` evidence. Test evidence preferred for WHY. Unresolved gaps marked `[NEEDS HUMAN INPUT]` only when code/tests do not support a claim. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Per-component README.md files | component usage | reference-only | Linked from Overview; not canonical | +| Root README.md | package usage | reference-only | GETTING_STARTED / ARCHITECTURE | +| Storybook stories | UI behavior | verified | Use Cases, UI Flow sections | + +## Overview + +The **components** module is the primary UI surface of `@webex/components`. It contains exported Webex experience components (meetings, messaging, roster, settings, authentication), shared generic widgets (`Button`, `Modal`, …), React hooks that read adapter observables, and HOCs (`withAdapter`, `withMeeting`) that wire adapter lifecycle into the tree. + +Internal folders (`icons/`, `inputs/`, `adaptive-cards/`, `generic/`) support exported components but are not all re-exported from `src/components/index.js`. + +## Purpose / Responsibility + +Owns all React presentation and client-side interaction for Webex component experiences. Does **not** fetch Webex cloud data directly — consumes injected adapters only. + +## Stack + +- JavaScript (JSX), React 18, PropTypes +- RxJS consumption via hooks (peer dependency) +- Jest + React Testing Library for tests +- SCSS co-located per component; aggregated via `src/styles/index.scss` + +## Folder / Package Structure + +``` +src/components/ +├── WebexMeeting/ # Full meeting shell +├── WebexMessaging/ # Messaging UI +├── WebexDataProvider/ # AdapterContext provider +├── hoc/ # withAdapter, withMeeting +├── hooks/ # useMeeting, usePerson, contexts +├── generic/ # Button, Modal, Title, … +├── icons/ # SVG/icon components +├── inputs/ # Form inputs +├── adaptive-cards/ # Adaptive card rendering +├── index.js # Public export barrel +├── helpers.js # className helpers +└── breakpoints.js # Responsive breakpoints +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `src/components/index.js` | Public component and hook exports | +| `src/components/hoc/withAdapter.jsx` | Adapter connect/disconnect HOC | +| `src/components/WebexDataProvider/WebexDataProvider.jsx` | AdapterContext provider | +| `src/components/hooks/contexts.js` | AdapterContext, MeetingContext | +| `src/constants.js` | `WEBEX_COMPONENTS_CLASS_PREFIX`, ARIA label constants | +| `src/util.js` | Shared helpers used by components/adapters | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| components.WebexMeeting | SDK export | `WebexMeeting` component | Default meeting UX | Semver beta | `src/components/WebexMeeting/WebexMeeting.jsx` | `ai-docs/CONTRACTS.md` | +| components.withAdapter | SDK export | HOC factory | Inject adapter + connect lifecycle | Semver | `src/components/hoc/withAdapter.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexDataProvider | SDK export | Context provider | Pass adapter to subtree | Semver | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `ai-docs/CONTRACTS.md` | +| components.useMeetingDestination | SDK export | Hook | Resolve meeting destination | Semver | `src/components/hooks/useMeetingDestination.js` | `ai-docs/CONTRACTS.md` | + +Full export list: `src/components/index.js`. + +## Requires (dependencies) + +- `@webex/component-adapter-interfaces` adapter instances on `AdapterContext` (activities, meetings, memberships, people, rooms adapters minimum per `WebexDataProvider.propTypes`) +- Peer: `react`, `react-dom`, `prop-types`, `rxjs` +- Internal: `src/adapters/` for Storybook/demo; production hosts use SDK adapters +- Styles from `src/styles/` imported at package entry + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| COMP-R-001 | Exported components MUST be listed in `src/components/index.js` | Defines npm public API | `src/components/index.js` | `package.json` exports via Rollup entry | none | PRESENT | +| COMP-R-002 | `withAdapter` MUST call `adapter.connect()` before wrapping with `WebexDataProvider` | Prevents hooks reading disconnected adapter | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | none | PRESENT | +| COMP-R-003 | `WebexDataProvider` MUST require adapter shape with five domain adapters | Matches adapter interface contract | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `src/components/WebexDataProvider/WebexDataProvider.test.js` | organizations adapter optional in propTypes | PRESENT | +| COMP-R-004 | DOM class prefix MUST use `wxc` constant | Consistent theming/CSS | `src/constants.js` | `src/styles/_variables.scss` | none | PRESENT | +| COMP-R-006 | All exported symbols MUST match `src/components/index.js` barrel | npm public API contract | `src/components/index.js` | build/export tests | none | PRESENT | +| COMP-R-007 | Internal hooks under `hooks/` MUST subscribe/unsubscribe adapter observables safely | Prevents memory leaks on unmount | `src/components/hooks/useMeeting.js` | hook tests | none | PRESENT | +| COMP-R-008 | `WebexDataProvider` PropTypes require five domain adapters (activities, meetings, memberships, people, rooms) | Documents minimum adapter shape for context | `src/components/WebexDataProvider/WebexDataProvider.jsx` | WebexDataProvider.test.js | organizationsAdapter on WebexJSONAdapter but not in PropTypes — host/SDK may still supply | PRESENT | + +## Design Overview + +Components follow a **container/presentation** split where data arrives exclusively through adapter hooks. `withAdapter` owns adapter instantiation and async connect/disconnect; presentational components remain testable with mock adapters in JSON form. + +Hooks encapsulate RxJS subscription lifecycle for meeting, person, room, and activity streams. Generic components (`Button`, `Modal`) provide shared UX primitives with Webex styling. + +## Data Flow + +```mermaid +flowchart TB + HostProps[Host props / access token] + Factory[adapterFactory in withAdapter] + Adapter[WebexAdapter instance] + Connect[adapter.connect] + Provider[WebexDataProvider] + Ctx[AdapterContext] + Hook[useMeeting / usePerson / …] + UI[Webex* component render] + + HostProps --> Factory + Factory --> Adapter + Adapter --> Connect + Connect --> Provider + Provider --> Ctx + Ctx --> Hook + Hook --> UI + UI -->|control action| Adapter +``` + +Data flows one way from adapter observables into hooks, then into React render output. User actions invoke adapter control objects (defined in adapter module). + +## Sequence Diagram(s) + +```mermaid +sequenceDiagram + participant Host + participant WithAdapter + participant Adapter + participant Provider as WebexDataProvider + participant Comp as WebexMeeting + + Host->>WithAdapter: render with props + WithAdapter->>Adapter: adapterFactory(props) + WithAdapter->>Adapter: connect() + Adapter-->>WithAdapter: connected + WithAdapter->>Provider: adapter + Provider->>Comp: AdapterContext available + Comp->>Adapter: subscribe meeting observables + Adapter-->>Comp: meeting state updates +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class withAdapter { + +WrappedComponent + +adapterFactory + } + class WebexDataProvider { + +adapter + +children + } + class WebexMeeting { + +meetingID + +layout + +controls + } + class useMeeting { + <> + } + withAdapter --> WebexDataProvider : wraps when connected + WebexDataProvider --> WebexMeeting : provides context + WebexMeeting --> useMeeting : reads state +``` + +Relationship: HOC → Provider → feature components → hooks → adapter interfaces (external package). + +## Use Cases + +| UC | Actor | Flow | Evidence | +|---|---|---|---| +| UC-1 Embed meeting | Host developer | Wrap route with `withAdapter(WebexMeeting, factory)` | `README.md`, stories | +| UC-2 Demo offline | Storybook author | Pass `WebexJSONAdapter` datasource | `.storybook/`, adapters module | +| UC-3 Custom controls | Host developer | Pass `controls` render prop to `WebexMeeting` | `src/components/WebexMeeting/WebexMeeting.jsx` | + +## UI Flow + +Primary meeting UI states driven by `MeetingState` from adapter interfaces: + +1. Authentication / waiting (guest/host auth components) +2. Interstitial (pre-join) +3. In-meeting (media, controls, roster) +4. Settings modal overlay + +Evidence: imports in `src/components/WebexMeeting/WebexMeeting.jsx`. + +## State Model + +| State layer | Owner | Transitions | +|---|---|---| +| Adapter observables | adapters module | Meeting join/leave, mute, roster — via control objects | +| React local state | components | Modal open, layout dimensions, UI-only toggles | +| AdapterContext | components | Set when `WebexDataProvider` mounts after connect | +| MeetingContext | components | Meeting-scoped subtree via `withMeeting` | + +No Redux/global store — all domain state flows from adapter subscriptions. + +Evidence: `src/components/hooks/contexts.js`, `src/components/hoc/withMeeting.jsx`, `src/adapters/MeetingsJSONAdapter.js`. + +## Concurrency & Reactive Flow + +Component hooks subscribe to RxJS observables from adapter methods. Subscriptions must clean up on unmount (handled in hook implementations). Multiple components may subscribe to the same meeting observable; adapter emits push updates. + +Evidence: `src/components/hooks/useMeeting.js`, `src/adapters/MeetingsJSONAdapter.js`, `rxjs` peer dependency. + +## Internal Components (supporting, not exported) + +| Component folder | Role | Used by | +|---|---|---| +| `WebexAdaptiveCard/` | Renders single adaptive card | `WebexAdaptiveCards` | +| `WebexAudioSettings/` | Audio device selection UI | `WebexSettings` | +| `WebexVideoSettings/` | Video device selection UI | `WebexSettings` | +| `WebexMeetingProvider/` | Provides meeting context | Meeting subtree | +| `WebexNoMedia/` | Placeholder when no media | Media components | +| `adaptive-cards/` | Adaptive card building blocks | Messaging/adaptive cards | +| `icons/` | SVG icon components | Generic and Webex components | +| `inputs/` | DateInput, Dropdown, TimeInput | Forms in components | +| `generic/` | Button, Modal, Badge, Spinner, … | Exported: Button, Modal; rest internal | + +Evidence: `src/components/` directory listing, import graph from `WebexMeeting.jsx`. + +## Hooks Catalog + +| Hook | Purpose | Evidence | +|---|---|---| +| `useMeeting` | Subscribe to current meeting observables | `src/components/hooks/useMeeting.js` | +| `useMeetingControl` | Access meeting control instances | `src/components/hooks/useMeetingControl.js` | +| `useMeetingDestination` | Resolve meeting destination (exported) | `src/components/hooks/useMeetingDestination.js` | +| `usePerson` | Person data by ID | `src/components/hooks/usePerson.js` | +| `useMe` | Current user person data | `src/components/hooks/useMe.js` | +| `useRoom` | Room/space data | `src/components/hooks/useRoom.js` | +| `useMembers` | Membership roster | `src/components/hooks/useMembers.js` | +| `useActivity` | Single activity | `src/components/hooks/useActivity.js` | +| `useActivityStream` | Activity stream | `src/components/hooks/useActivityStream.js` | +| `useActivityScroll` | Scroll behavior for activities | `src/components/hooks/useActivityScroll.js` | +| `useOverflowActivities` | Overflow activity handling | `src/components/hooks/useOverflowActivities.js` | +| `useAdaptiveCard` | Adaptive card state | `src/components/hooks/useAdaptiveCard.js` | +| `useOrganization` | Organization data | `src/components/hooks/useOrganization.js` | +| `useStream` | Media stream helper | `src/components/hooks/useStream.js` | +| `useSpeakers` | Speaker list | `src/components/hooks/useSpeakers.js` | +| `useMetrics` | Metrics callback hook | `src/components/hooks/useMetrics.js` | +| `useElementDimensions` | Element size measurement | `src/components/hooks/useElementDimensions.js` | +| `useElementPosition` | Element position | `src/components/hooks/useElementPosition.js` | +| `useAutoFocus` | Focus management | `src/components/hooks/useAutoFocus.js` | +| `useRef` | Ref helper | `src/components/hooks/useRef.js` | + +Barrel: `src/components/hooks/index.js`. Only `useMeetingDestination` exported from package barrel. + +## Module Conventions + +- One folder per component: `ComponentName/ComponentName.jsx` + co-located `.scss`, `.test.js`, `.stories.js`. +- Use `webexComponentClasses()` from `src/components/helpers.js` for BEM-style class names. +- Responsive breakpoints from `src/components/breakpoints.js` (e.g. `PHONE_LARGE`). +- JSDoc on all public props and adapter-facing methods per `CONTRIBUTING.md`. + +## Design Trade-offs + +- **Adapter injection vs bundled SDK:** Keeps package lightweight; hosts choose data source at cost of integration complexity. +- **JSON adapters in-repo:** Enables Storybook/offline demo without mocking entire SDK. +- **Side-effect style import:** `src/index.js` imports SCSS globally — simplifies consumer CSS at cost of bundler side-effect awareness. + +## Host Integration + +Hosts MUST: + +1. Install peer dependencies (`react`, `react-dom`, `rxjs`, `prop-types`). +2. Import compiled CSS from package `dist/css/`. +3. Provide adapter via `withAdapter` or manual `WebexDataProvider`. +4. For production Webex data, use SDK adapter from `@webex/sdk-component-adapter` (external repo) — not bundled here. + +Evidence: `README.md`, `package.json` peerDependencies. + +## Export Stability + +Public exports are semver-managed via semantic-release. Beta status documented in README — consumers should pin versions. + +Evidence: `README.md` Project Status, `package.json` release scripts. + +## Error Handling & Failure Modes + +- Adapter not connected: `withAdapter` renders wrapped component without provider until connect completes. +- Missing meeting ID: components rely on adapter/state; hosts must pass valid IDs. +- Invalid URLs: use `isValidUrl` helper where links accepted. + +Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. + +## Pitfalls + +- Importing components without CSS yields unstyled UI — load `dist/css/webex-components.css`. +- Assuming `organizationsAdapter` on provider when only five adapters validated in propTypes — verify host adapter shape. +- Snapshot tests sensitive to class names — coordinate with styles module when changing prefix. + +## Test-Case Strategy (module) + +- Co-located Jest tests per component (`*.test.js`, `*.test.jsx`). +- Snapshot tests for stable DOM structure where established. +- Hook tests with mock adapter context. +- Storybook for visual states; Chromatic in CI. + +Evidence: `src/components/**/*.test.js`, `.circleci/config.yml`. + +## Traceability + +| Requirement | Code | Tests | +|---|---|---| +| COMP-R-001 | `src/components/index.js` | Rollup build smoke | +| COMP-R-002 | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | +| COMP-R-003 | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `src/components/WebexDataProvider/WebexDataProvider.test.js` | +| COMP-R-004 | `src/constants.js` | style/component tests | +| COMP-R-005 | `src/components/index.js` | export barrel tests / build | +| COMP-R-006 | `src/components/index.js` | build | +| COMP-R-007 | `src/components/hooks/useMeeting.js` | hook tests | +| COMP-R-008 | `src/components/WebexDataProvider/WebexDataProvider.jsx` | WebexDataProvider.test.js | diff --git a/src/styles/ai-docs/styles-themes-spec.md b/src/styles/ai-docs/styles-themes-spec.md new file mode 100644 index 000000000..c12000488 --- /dev/null +++ b/src/styles/ai-docs/styles-themes-spec.md @@ -0,0 +1,230 @@ +# styles-themes — SPEC + +> Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | styles-themes | +| Source path(s) | `src/styles/`, `src/themes/`, `src/assets/` | +| Doc kind | Module spec | +| Coverage score | 95% assessed 2026-07-23 — SCSS graph, themes, fonts, and build outputs documented | +| Generated from | `module-spec` @ SDLC template library `0.2.1` | +| generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | +| Validation status | not-run | + +## Evidence Rules + +Style requirements cite SCSS entry files and Rollup copy targets. Class prefix cross-checks `src/constants.js`. + +## Source Material Register + +| Source material | Scope | Decision | Detail location | +|---|---|---|---| +| Component `_components.scss` | per-component styles | verified | Imported via `src/styles/index.scss` | +| Storybook | visual verification | reference-only | `.storybook/` | + +## Overview + +The **styles-themes** module defines global SCSS (variables, mixins, colors, fonts, defaults), dark/light theme token files, and font assets copied into `dist/` during build. Package entry `src/index.js` side-imports `src/styles/index.scss` so bundlers emit consolidated CSS. + +## Purpose / Responsibility + +Own visual consistency for Webex Components (`wxc-` class prefix) and deliver compiled CSS + theme assets to npm consumers. Does **not** implement component logic. + +## Stack + +- SCSS (sass) +- Rollup `rollup-plugin-scss` → `dist/css/webex-components.css` +- Rollup `rollup-plugin-copy` for themes and fonts + +## Folder / Package Structure + +``` +src/styles/ +├── index.scss # Entry: imports mixins, variables, themes, components +├── _variables.scss # SCSS variables (incl. class prefix) +├── _colors.scss +├── _mixins.scss +├── _fonts.scss +└── _defaults.scss + +src/themes/ +├── dark.scss +├── light.scss +├── dark/webex-logo.svg +└── light/webex-logo.svg + +src/assets/fonts/ # Copied to dist/assets on build +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `src/styles/index.scss` | SCSS import graph root | +| `src/styles/_variables.scss` | Design tokens; class prefix must match `src/constants.js` | +| `src/themes/dark.scss` | Dark theme variables | +| `src/themes/light.scss` | Light theme variables | +| `rollup.config.js` | SCSS output path and copy targets | +| `src/constants.js` | `WEBEX_COMPONENTS_CLASS_PREFIX = 'wxc'` | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility | Detail link | +|---|---|---|---|---|---| +| styles.css.bundle | npm asset | `dist/css/webex-components.css` | Compiled styles for hosts | Additive class changes preferred | `rollup.config.js` | +| styles.themes.dark | npm asset | `dist/themes/dark/` | Dark theme static assets | Semver | `rollup.config.js` | +| styles.themes.light | npm asset | `dist/themes/light/` | Light theme assets | Semver | `rollup.config.js` | +| styles.fonts | npm asset | `dist/assets/fonts/` | Font files | Semver | `rollup.config.js` | + +Internal Surface — SCSS partials are build-time, not direct public API. + +## Requires (dependencies) + +- `node-sass` / sass toolchain (devDependency via build) +- Component SCSS partials under `src/components/_components.scss` and per-component styles + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test Evidence | Gaps | Confidence | +|---|---|---|---|---|---|---| +| STY-R-001 | Package entry MUST import global SCSS | Ensures CSS emitted in library build | `src/index.js` | build output | none | PRESENT | +| STY-R-002 | Class prefix in SCSS MUST match JS constant `wxc` | Prevents broken selectors | `src/styles/_variables.scss`, `src/constants.js` | component tests | none | PRESENT | +| STY-R-003 | Rollup MUST emit compressed CSS to `dist/css/webex-components.css` | Consumer import path | `rollup.config.js` | CI build | none | PRESENT | +| STY-R-004 | Build MUST copy theme folders to `dist/themes/` | Host theme switching | `rollup.config.js` copy plugin | build artifact | none | PRESENT | +| STY-R-006 | Theme SCSS files MUST be imported from `src/styles/index.scss` | Single CSS bundle | `src/styles/index.scss` | build | none | PRESENT | +| STY-R-007 | Component styles aggregated via `src/components/_components.scss` | Component-level SCSS inclusion | `src/components/_components.scss` | build | none | PRESENT | + +## SCSS File Registry + +| File | Role | +|---|---| +| `src/styles/index.scss` | Entry import graph | +| `src/styles/_variables.scss` | Prefix, fonts, meeting min dimensions | +| `src/styles/_colors.scss` | Color tokens | +| `src/styles/_mixins.scss` | Shared mixins | +| `src/styles/_fonts.scss` | Font face declarations | +| `src/styles/_defaults.scss` | Base element defaults | +| `src/themes/dark.scss` | Dark theme tokens | +| `src/themes/light.scss` | Light theme tokens | +| `src/components/_components.scss` | Imports all component SCSS partials | + +## Theme & Asset Assets + +| Path | Role | +|---|---| +| `src/themes/dark/webex-logo.svg` | Dark theme logo | +| `src/themes/light/webex-logo.svg` | Light theme logo | +| `src/assets/fonts/` | CiscoSansTT font files copied to `dist/assets/fonts/` | + +## Module Conventions + +- SCSS partials prefixed with `_` under `src/styles/`. +- Class prefix variable `$WEBEX_COMPONENTS_CLASS_PREFIX` must match `src/constants.js`. +- Component SCSS lives beside component JSX; registered through `_components.scss`. + +## Design Trade-offs + +- **Single compressed CSS bundle:** Simple host integration vs inability to tree-shake unused component styles. +- **Copy vs inline theme assets:** Theme folders copied verbatim for host static serving flexibility. + +## Design Overview + +SCSS is organized in layers: variables/mixins → color/font tokens → theme overrides → component aggregation. Themes (`dark.scss`, `light.scss`) set CSS custom properties or SCSS variables consumed by component rules. Rollup compiles a single CSS bundle for simplicity; theme folders ship as static assets for logos and theme-specific resources. + +## Data Flow + +```mermaid +flowchart LR + Index[src/index.js import scss] + Styles[src/styles/index.scss] + Components[src/components/_components.scss] + Rollup[rollup-plugin-scss] + CSS[dist/css/webex-components.css] + Copy[rollup-plugin-copy] + DistThemes[dist/themes] + DistFonts[dist/assets/fonts] + + Index --> Styles + Styles --> Components + Styles --> Rollup + Rollup --> CSS + Copy --> DistThemes + Copy --> DistFonts +``` + +## Sequence Diagram(s) + +```mermaid +sequenceDiagram + participant Dev as Developer + participant Rollup + participant SCSS as rollup-plugin-scss + participant Copy as rollup-plugin-copy + + Dev->>Rollup: npm run build + Rollup->>SCSS: compile src/index.js graph + SCSS-->>Rollup: dist/css/webex-components.css + Rollup->>Copy: copy themes + fonts + Copy-->>Rollup: dist/themes, dist/assets +``` + +## Class / Component Relationships + +- SCSS `_variables.scss` defines `$wxc-*` tokens consumed by component SCSS files. +- `helpers.js` `webexComponentClasses()` generates BEM-style class strings aligned with SCSS blocks. +- No OOP classes in this module — relationship is **token → component stylesheet → rendered DOM class**. + +Evidence: `src/components/helpers.js`, `src/styles/_variables.scss`. + +## Use Cases + +| UC | Description | Evidence | +|---|---|---| +| UC-1 Consumer styling | Host imports `dist/css/webex-components.css` | `README.md` | +| UC-2 Theme assets | Host serves `dist/themes/dark` logos | `rollup.config.js` | +| UC-3 Component author | Add SCSS beside new component; register in `_components.scss` | `src/components/` patterns | + +## Host Integration + +Hosts load compiled CSS once globally. Theme selection may swap CSS variables or load theme-specific assets from `dist/themes/`. Class prefix `wxc` must not be overridden without updating both JS and SCSS. + +## Error Handling & Failure Modes + +- SCSS compilation failure aborts build (`failOnError: true` in Rollup scss plugin). +- Missing theme/font copy targets fail at build time via Rollup copy plugin. +- Host omitting CSS import results in unstyled components — see components spec pitfalls. + +Evidence: `rollup.config.js`. + +## Export Stability + +Published npm assets (`dist/css/`, `dist/themes/`, `dist/assets/`) follow semver via semantic-release. Prefer additive CSS classes; renaming `wxc-` prefixed classes is breaking. + +Evidence: `package.json`, `README.md`. + +## Pitfalls + +- Changing `$prefix` in SCSS without updating `WEBEX_COMPONENTS_CLASS_PREFIX` breaks tests and styles. +- SCSS compilation failures fail build (`failOnError: true` in Rollup scss plugin). +- Consumers who tree-shake JS but omit CSS see unstyled components. + +Evidence: `rollup.config.js`, `src/constants.js`. + +## Test-Case Strategy (module) + +- Indirect: component snapshot tests assert expected `wxc-` classes. +- Build verification: `npm run build` produces expected dist artifacts. +- Visual: Storybook/Chromatic for theme regressions. + +## Traceability + +| Requirement | Code | Tests | +|---|---|---| +| STY-R-001 | `src/index.js` | build | +| STY-R-002 | `src/styles/_variables.scss`, `src/constants.js` | component snapshots | +| STY-R-003 | `rollup.config.js` | CI build | +| STY-R-004 | `rollup.config.js` | CI build | +| STY-R-005 | `rollup.config.js` | CI build | From f9f0f35ebb6a11c7a2b526e8e1e62ffd8f17a6a4 Mon Sep 17 00:00:00 2001 From: Akula Uday Date: Thu, 23 Jul 2026 13:57:31 +0530 Subject: [PATCH 2/4] docs(sdd): fix adapter export and control registry drift Co-authored-by: Cursor --- .sdd/manifest.json | 4 +-- ai-docs/CONTRACTS.md | 25 +++++++++++++------ src/adapters/ai-docs/adapters-spec.md | 36 +++++++++++++++++++-------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/.sdd/manifest.json b/.sdd/manifest.json index 2c8f8f018..3eae23828 100644 --- a/.sdd/manifest.json +++ b/.sdd/manifest.json @@ -85,8 +85,8 @@ "canonical_spec": "src/adapters/ai-docs/adapters-spec.md", "contracts": { "provides": [ - "WebexJSONAdapter façade", - "Domain JSON adapters: meetings, people, rooms, activities, memberships, organizations" + "WebexJSONAdapter façade (npm public export via src/index.js)", + "Internal domain JSON adapters composed by façade: meetings, people, rooms, activities, memberships, organizations" ], "requires": [ "@webex/component-adapter-interfaces", diff --git a/ai-docs/CONTRACTS.md b/ai-docs/CONTRACTS.md index 004a96fe2..832169f8e 100644 --- a/ai-docs/CONTRACTS.md +++ b/ai-docs/CONTRACTS.md @@ -1,18 +1,12 @@ # Contracts Catalog — webex/components -> Machine source: `.sdd/manifest.json`. Full export barrels: `src/index.js`, `src/components/index.js`, `src/adapters/index.js`. +> Machine source: `.sdd/manifest.json`. Package entry: `src/index.js` (Rollup input). Component barrel: `src/components/index.js`. Adapter module barrel: `src/adapters/index.js` (internal — only `WebexJSONAdapter` is re-exported from the package root). ### Exported API & Types | Contract ID | Owner | Symbol | Purpose | Stability | Defined at | |---|---|---|---|---|---| -| pkg.WebexJSONAdapter | adapters | WebexJSONAdapter | JSON adapter façade | Semver beta | `src/adapters/index.js` | -| pkg.ActivitiesJSONAdapter | adapters | ActivitiesJSONAdapter | Activities domain | Semver beta | `src/adapters/index.js` | -| pkg.MeetingsJSONAdapter | adapters | MeetingsJSONAdapter | Meetings domain | Semver beta | `src/adapters/index.js` | -| pkg.MembershipJSONAdapter | adapters | MembershipJSONAdapter | Memberships domain | Semver beta | `src/adapters/index.js` | -| pkg.OrganizationsJSONAdapter | adapters | OrganizationsJSONAdapter | Organizations domain | Semver beta | `src/adapters/index.js` | -| pkg.PeopleJSONAdapter | adapters | PeopleJSONAdapter | People domain | Semver beta | `src/adapters/index.js` | -| pkg.RoomsJSONAdapter | adapters | RoomsJSONAdapter | Rooms domain | Semver beta | `src/adapters/index.js` | +| pkg.WebexJSONAdapter | adapters | WebexJSONAdapter | JSON adapter façade (npm public API) | Semver beta | `src/index.js` | | pkg.WEBEX_COMPONENTS_CLASS_PREFIX | components | WEBEX_COMPONENTS_CLASS_PREFIX | CSS class prefix `'wxc'` | Stable | `src/index.js` | | pkg.WebexAvatar | components | WebexAvatar | Avatar display | Semver beta | `src/components/index.js` | | pkg.WebexActivity | components | WebexActivity | Single activity | Semver beta | `src/components/index.js` | @@ -48,6 +42,21 @@ | pkg.css.bundle | styles-themes | webex-components.css | Compiled stylesheet | Semver beta | `dist/css/webex-components.css` | | pkg.themes | styles-themes | dist/themes/* | Theme asset folders | Semver beta | `rollup.config.js` | +### Internal adapter classes (not npm package exports) + +Composed by `WebexJSONAdapter` and exported from the adapter module barrel for tests/Storybook — **not** re-exported from `src/index.js`. + +| Symbol | Purpose | Defined at | +|---|---|---| +| ActivitiesJSONAdapter | Activities domain | `src/adapters/ActivitiesJSONAdapter.js` | +| MeetingsJSONAdapter | Meetings domain + controls | `src/adapters/MeetingsJSONAdapter.js` | +| MembershipJSONAdapter | Memberships domain | `src/adapters/MembershipJSONAdapter.js` | +| OrganizationsJSONAdapter | Organizations domain | `src/adapters/OrganizationsJSONAdapter.js` | +| PeopleJSONAdapter | People domain | `src/adapters/PeopleJSONAdapter.js` | +| RoomsJSONAdapter | Rooms domain | `src/adapters/RoomsJSONAdapter.js` | + +Evidence: `src/adapters/index.js` vs `src/index.js` (only `WebexJSONAdapter` is published). + ### Internal-only components (not in public barrel) | Symbol | Purpose | Defined at | diff --git a/src/adapters/ai-docs/adapters-spec.md b/src/adapters/ai-docs/adapters-spec.md index 3365ba676..83cc319ee 100644 --- a/src/adapters/ai-docs/adapters-spec.md +++ b/src/adapters/ai-docs/adapters-spec.md @@ -54,7 +54,7 @@ src/adapters/ ├── ActivitiesJSONAdapter.js ├── MembershipJSONAdapter.js ├── OrganizationsJSONAdapter.js -├── index.js # Public exports +├── index.js # Module barrel (internal; not package root) └── *.test.js # Domain adapter tests ``` @@ -65,20 +65,29 @@ src/adapters/ | `src/adapters/WebexJSONAdapter.js` | Façade wiring datasource keys to domain adapters | | `src/adapters/MeetingsJSONAdapter.js` | Meeting state machine, observables, control registry | | `src/adapters/MeetingsJSONAdapter/controls/index.js` | Control exports | -| `src/adapters/index.js` | Public adapter exports | +| `src/adapters/index.js` | Adapter module barrel (internal); only `WebexJSONAdapter` is npm-exported via `src/index.js` | | `src/util.js` | `deepMerge` used by MeetingsJSONAdapter | ## Public Surface +Only `WebexJSONAdapter` is published from `@webex/components` (`src/index.js` → Rollup package entry). Domain adapters are internal implementation composed by the façade. + | Contract ID | Type | Surface | Purpose | Compatibility | Detail link | Root index | |---|---|---|---|---|---|---| -| adapters.WebexJSONAdapter | SDK export | class | Entry JSON adapter | Semver | `src/adapters/WebexJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.MeetingsJSONAdapter | SDK export | class | Meeting observables + controls | Semver | `src/adapters/MeetingsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.PeopleJSONAdapter | SDK export | class | People search/display data | Semver | `src/adapters/PeopleJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.RoomsJSONAdapter | SDK export | class | Space/room data | Semver | `src/adapters/RoomsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.ActivitiesJSONAdapter | SDK export | class | Activity stream data | Semver | `src/adapters/ActivitiesJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.MembershipJSONAdapter | SDK export | class | Membership/roster data | Semver | `src/adapters/MembershipJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -| adapters.OrganizationsJSONAdapter | SDK export | class | Organization data | Semver | `src/adapters/OrganizationsJSONAdapter.js` | `ai-docs/CONTRACTS.md` | +| adapters.WebexJSONAdapter | npm package export | class | Entry JSON adapter façade | Semver | `src/adapters/WebexJSONAdapter.js` | `ai-docs/CONTRACTS.md` | + +### Internal adapter-barrel exports (not npm package API) + +Exported from `src/adapters/index.js` for module tests and Storybook; accessed in apps via `WebexJSONAdapter` domain properties (`meetingsAdapter`, `peopleAdapter`, …). + +| Contract ID | Type | Surface | Purpose | Detail link | +|---|---|---|---|---| +| adapters.MeetingsJSONAdapter | internal | class | Meeting observables + controls | `src/adapters/MeetingsJSONAdapter.js` | +| adapters.PeopleJSONAdapter | internal | class | People search/display data | `src/adapters/PeopleJSONAdapter.js` | +| adapters.RoomsJSONAdapter | internal | class | Space/room data | `src/adapters/RoomsJSONAdapter.js` | +| adapters.ActivitiesJSONAdapter | internal | class | Activity stream data | `src/adapters/ActivitiesJSONAdapter.js` | +| adapters.MembershipJSONAdapter | internal | class | Membership/roster data | `src/adapters/MembershipJSONAdapter.js` | +| adapters.OrganizationsJSONAdapter | internal | class | Organization data | `src/adapters/OrganizationsJSONAdapter.js` | ## Requires (dependencies) @@ -115,10 +124,17 @@ Evidence: `src/adapters/WebexJSONAdapter.js` constructor. | `SWITCH_MICROPHONE_CONTROL` | `SwitchMicrophoneControl` | Switch microphone | | `SWITCH_SPEAKER_CONTROL` | `SwitchSpeakerControl` | Switch speaker | | `DISABLED_MUTE_AUDIO_CONTROL` | `DisabledMuteAudioControl` | Disabled mute state | -| `DISABLED_JOIN_CONTROL` | (disabled join) | Disabled join state | Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/`. +### Defined control constants not registered in `meetingControls` + +| Constant | ID string | Status | +|---|---|---| +| `DISABLED_JOIN_CONTROL` | `disabled-join-meeting` | Exported constant only — no control class under `MeetingsJSONAdapter/controls/` and not instantiated in `meetingControls`; `supportedControls()` omits this ID | + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter.test.js`. + ## Domain Adapter Summary | Adapter | Source file | Test file | From c0140453d4137b7b78bb5656bd13c3e13215d82b Mon Sep 17 00:00:00 2001 From: Akula Uday Date: Thu, 23 Jul 2026 14:15:27 +0530 Subject: [PATCH 3/4] docs(sdd): reorder module specs and fix validation findings Co-authored-by: Cursor --- src/adapters/ai-docs/adapters-spec.md | 142 ++++++++++++--------- src/components/ai-docs/components-spec.md | 149 ++++++++++++---------- src/styles/ai-docs/styles-themes-spec.md | 104 ++++++++------- 3 files changed, 221 insertions(+), 174 deletions(-) diff --git a/src/adapters/ai-docs/adapters-spec.md b/src/adapters/ai-docs/adapters-spec.md index 83cc319ee..426bba5c2 100644 --- a/src/adapters/ai-docs/adapters-spec.md +++ b/src/adapters/ai-docs/adapters-spec.md @@ -23,7 +23,7 @@ Requirements cite adapter source and co-located `*.test.js` files. Observable be | Source material | Scope | Decision | Detail location | |---|---|---|---| | `@webex/component-adapter-interfaces` | adapter API | reference-only | Requires section; npm package | -| JSON fixtures | demo data | verified | `src/data/*.json` if present | +| Datasource modules | demo data | verified | `src/data/*.js` (`activities.js`, `meetings.js`, …) | ## Overview @@ -76,9 +76,7 @@ Only `WebexJSONAdapter` is published from `@webex/components` (`src/index.js` |---|---|---|---|---|---|---| | adapters.WebexJSONAdapter | npm package export | class | Entry JSON adapter façade | Semver | `src/adapters/WebexJSONAdapter.js` | `ai-docs/CONTRACTS.md` | -### Internal adapter-barrel exports (not npm package API) - -Exported from `src/adapters/index.js` for module tests and Storybook; accessed in apps via `WebexJSONAdapter` domain properties (`meetingsAdapter`, `peopleAdapter`, …). +**Internal adapter-barrel exports (not npm package API)** — exported from `src/adapters/index.js` for module tests and Storybook; accessed in apps via `WebexJSONAdapter` domain properties (`meetingsAdapter`, `peopleAdapter`, …): | Contract ID | Type | Surface | Purpose | Detail link | |---|---|---|---|---| @@ -106,58 +104,10 @@ Evidence: `src/adapters/WebexJSONAdapter.js` constructor. | ADP-R-002 | `connect()` / `disconnect()` MUST resolve immediately for JSON adapters | No async network | `src/adapters/WebexJSONAdapter.js` | — | none | PRESENT | | ADP-R-003 | `MeetingsJSONAdapter` MUST expose meeting control constants (join, mute, share, …) | UI maps controls by ID | `src/adapters/MeetingsJSONAdapter.js` | `src/adapters/MeetingsJSONAdapter.test.js` | none | PRESENT | | ADP-R-004 | Meeting state updates MUST use RxJS observables | Components subscribe via hooks | `src/adapters/MeetingsJSONAdapter.js` | MeetingsJSONAdapter tests | none | PRESENT | +| ADP-R-005 | Each registered meeting control MUST map to a control class instance in `meetingControls` | Components invoke controls by ID | `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/` | `MeetingsJSONAdapter.test.js` | `DISABLED_JOIN_CONTROL` defined but not registered | PRESENT | | ADP-R-006 | Datasource MUST provide keys: activities, meetings, memberships, organizations, people, rooms | WebexJSONAdapter constructor contract | `src/adapters/WebexJSONAdapter.js` | adapter tests | none | PRESENT | | ADP-R-007 | Each domain adapter MUST extend matching interface adapter class | Interface compliance | `src/adapters/PeopleJSONAdapter.js` | PeopleJSONAdapter.test.js | none | PRESENT | -## Meeting Controls Registry - -| Control constant | Control class | Purpose | -|---|---|---| -| `JOIN_CONTROL` | `JoinControl` | Join meeting | -| `LEAVE_CONTROL` | `LeaveControl` | Leave meeting | -| `MUTE_AUDIO_CONTROL` | `MuteAudioControl` | Toggle audio mute | -| `MUTE_VIDEO_CONTROL` | `MuteVideoControl` | Toggle video mute | -| `SHARE_CONTROL` | `ShareControl` | Screen share | -| `ROSTER_CONTROL` | `RosterControl` | Toggle roster | -| `SETTINGS_CONTROL` | `SettingsControl` | Open settings | -| `SWITCH_CAMERA_CONTROL` | `SwitchCameraControl` | Switch camera device | -| `SWITCH_MICROPHONE_CONTROL` | `SwitchMicrophoneControl` | Switch microphone | -| `SWITCH_SPEAKER_CONTROL` | `SwitchSpeakerControl` | Switch speaker | -| `DISABLED_MUTE_AUDIO_CONTROL` | `DisabledMuteAudioControl` | Disabled mute state | - -Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/`. - -### Defined control constants not registered in `meetingControls` - -| Constant | ID string | Status | -|---|---|---| -| `DISABLED_JOIN_CONTROL` | `disabled-join-meeting` | Exported constant only — no control class under `MeetingsJSONAdapter/controls/` and not instantiated in `meetingControls`; `supportedControls()` omits this ID | - -Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter.test.js`. - -## Domain Adapter Summary - -| Adapter | Source file | Test file | -|---|---|---| -| ActivitiesJSONAdapter | `src/adapters/ActivitiesJSONAdapter.js` | `ActivitiesJSONAdapter.test.js` | -| MeetingsJSONAdapter | `src/adapters/MeetingsJSONAdapter.js` | `MeetingsJSONAdapter.test.js` | -| MembershipJSONAdapter | `src/adapters/MembershipJSONAdapter.js` | `MembershipJSONAdapter.test.js` | -| OrganizationsJSONAdapter | `src/adapters/OrganizationsJSONAdapter.js` | `OrganizationsJSONAdapter.test.js` | -| PeopleJSONAdapter | `src/adapters/PeopleJSONAdapter.js` | `PeopleJSONAdapter.test.js` | -| RoomsJSONAdapter | `src/adapters/RoomsJSONAdapter.js` | `RoomsJSONAdapter.test.js` | - -## Module Conventions - -- One adapter class per domain JSON file at `src/adapters/` root. -- Meeting controls as separate classes under `MeetingsJSONAdapter/controls/`. -- Use RxJS `Observable` for all adapter data streams. -- Use `deepMerge` from `src/util.js` for immutable-style state updates in meetings adapter. - -## Design Trade-offs - -- **In-memory JSON vs live SDK:** Simplifies Storybook/tests; behavior may diverge from production SDK adapter edge cases — hosts must validate against SDK adapter for production. -- **Synchronous connect/disconnect:** JSON adapter resolves immediately; SDK adapters may differ in timing — `withAdapter` handles async connect generically. - ## Design Overview JSON adapters mirror the SDK adapter shape so components do not branch on data source. `WebexJSONAdapter` delegates to focused adapters per domain. `MeetingsJSONAdapter` centralizes meeting lifecycle (NOT_JOINED → joined states), media permissions, roster visibility, and settings preview — emitting updates through observables consumed by component hooks. @@ -186,6 +136,12 @@ flowchart LR ## Sequence Diagram(s) +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| JSON adapter connect + join control | below | invalid meeting ID handled per control tests | + ```mermaid sequenceDiagram participant Story as Storybook/Host @@ -231,10 +187,22 @@ classDiagram | UC | Description | Evidence | |---|---|---| -| UC-1 Storybook demo | Instantiate `WebexJSONAdapter` with static JSON | `.storybook/` stories | +| UC-1 Storybook demo | Instantiate `WebexJSONAdapter` with static JSON from `src/data/*.js` | `.storybook/` stories | | UC-2 Unit test | Inject JSON adapter into `WebexDataProvider` | component tests | | UC-3 Control interaction | User clicks join → `JoinControl` updates state | `MeetingsJSONAdapter/controls/JoinControl.js` | +## State Model + +In-memory meeting and domain records live on the JSON datasource object passed to `WebexJSONAdapter`. `MeetingsJSONAdapter` mutates meeting entries via `deepMerge` when controls fire; other domain adapters read/write their respective datasource slices. + +| State store | Owner | Notes | +|---|---|---| +| Datasource object | host/test/Storybook | Keys: activities, meetings, memberships, organizations, people, rooms | +| Meeting entries | MeetingsJSONAdapter | Status, media flags, roster visibility updated by controls | +| Observable caches | per adapter method | RxJS streams emit on mutation | + +Evidence: `src/adapters/WebexJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter.js`, `src/util.js`. + ## Concurrency & Reactive Flow Meeting updates propagate via RxJS `Observable` streams. Multiple subscribers (hooks) share hot/cold patterns as implemented per adapter method — components must unsubscribe on unmount (handled in hooks). @@ -247,19 +215,63 @@ Meeting status transitions include `NOT_JOINED` and joined/in-meeting states ali Evidence: `src/adapters/MeetingsJSONAdapter.js` `EMPTY_MEETING`, status field. -## Error Handling & Failure Modes +## Meeting Controls Registry + +Registered controls instantiated in `meetingControls`: + +| Control constant | Control class | Purpose | +|---|---|---| +| `JOIN_CONTROL` | `JoinControl` | Join meeting | +| `LEAVE_CONTROL` | `LeaveControl` | Leave meeting | +| `MUTE_AUDIO_CONTROL` | `MuteAudioControl` | Toggle audio mute | +| `MUTE_VIDEO_CONTROL` | `MuteVideoControl` | Toggle video mute | +| `SHARE_CONTROL` | `ShareControl` | Screen share | +| `ROSTER_CONTROL` | `RosterControl` | Toggle roster | +| `SETTINGS_CONTROL` | `SettingsControl` | Open settings | +| `SWITCH_CAMERA_CONTROL` | `SwitchCameraControl` | Switch camera device | +| `SWITCH_MICROPHONE_CONTROL` | `SwitchMicrophoneControl` | Switch microphone | +| `SWITCH_SPEAKER_CONTROL` | `SwitchSpeakerControl` | Switch speaker | +| `DISABLED_MUTE_AUDIO_CONTROL` | `DisabledMuteAudioControl` | Disabled mute state | + +**Defined constants not registered in `meetingControls`:** -- JSON adapters resolve connect/disconnect without error paths for network. -- Invalid datasource keys may yield undefined adapter data — hosts must supply complete datasource shape. -- Control operations on invalid meeting IDs: behavior defined per control implementation; verify tests. +| Constant | ID string | Status | +|---|---|---| +| `DISABLED_JOIN_CONTROL` | `disabled-join-meeting` | Exported constant only — no control class and not in `supportedControls()` | + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/`, `src/adapters/MeetingsJSONAdapter.test.js`. + +## Domain Adapter Summary + +| Adapter | Source file | Test file | +|---|---|---| +| ActivitiesJSONAdapter | `src/adapters/ActivitiesJSONAdapter.js` | `ActivitiesJSONAdapter.test.js` | +| MeetingsJSONAdapter | `src/adapters/MeetingsJSONAdapter.js` | `MeetingsJSONAdapter.test.js` | +| MembershipJSONAdapter | `src/adapters/MembershipJSONAdapter.js` | `MembershipJSONAdapter.test.js` | +| OrganizationsJSONAdapter | `src/adapters/OrganizationsJSONAdapter.js` | `OrganizationsJSONAdapter.test.js` | +| PeopleJSONAdapter | `src/adapters/PeopleJSONAdapter.js` | `PeopleJSONAdapter.test.js` | +| RoomsJSONAdapter | `src/adapters/RoomsJSONAdapter.js` | `RoomsJSONAdapter.test.js` | ## Pitfalls - Datasource must include all keys expected by `WebexJSONAdapter` constructor. - Meeting control IDs are string constants — must stay aligned with component control bar mapping. - `deepMerge` mutates destination objects — callers must pass clones if immutability required. +- Do not treat domain adapter classes as npm public API — only `WebexJSONAdapter` is published from `src/index.js`. + +Evidence: `src/adapters/WebexJSONAdapter.js`, `src/util.js`, `src/index.js`. + +## Export Stability + +Only `WebexJSONAdapter` is a semver-managed npm export from `@webex/components`. Internal domain adapter classes may change without a major bump as long as the façade shape and `@webex/component-adapter-interfaces` compliance are preserved. + +Evidence: `src/index.js`, `ai-docs/CONTRACTS.md`. -Evidence: `src/adapters/WebexJSONAdapter.js`, `src/util.js`. +## Host Integration & Theming + +Hosts typically construct `WebexJSONAdapter` with a datasource object (often from `src/data/*.js` modules in demos) and pass the instance to `withAdapter` or `WebexDataProvider`. Production hosts should use `@webex/sdk-component-adapter` instead of JSON adapters. + +Evidence: `README.md`, `.storybook/`, `src/data/index.js`. ## Test-Case Strategy (module) @@ -267,6 +279,11 @@ Evidence: `src/adapters/WebexJSONAdapter.js`, `src/util.js`. - Assert observable emissions and control side effects. - Mock datasource objects inline in tests. +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| ADP-R-005 control registry | `MeetingsJSONAdapter.test.js` | none for unregistered `DISABLED_JOIN_CONTROL` | +| ADP-R-003 control constants | `MeetingsJSONAdapter.test.js` | none | + Evidence: `src/adapters/*.test.js`. ## Traceability @@ -277,4 +294,9 @@ Evidence: `src/adapters/*.test.js`. | ADP-R-002 | `src/adapters/WebexJSONAdapter.js` | — | | ADP-R-003 | `src/adapters/MeetingsJSONAdapter.js` | `MeetingsJSONAdapter.test.js` | | ADP-R-004 | `src/adapters/MeetingsJSONAdapter.js` | MeetingsJSONAdapter tests | -| ADP-R-005 | `src/adapters/MeetingsJSONAdapter/controls/` | control tests | +| ADP-R-005 | `src/adapters/MeetingsJSONAdapter/controls/` | `MeetingsJSONAdapter.test.js` | +| ADP-R-006 | `src/adapters/WebexJSONAdapter.js` | adapter tests | +| ADP-R-007 | `src/adapters/PeopleJSONAdapter.js` | PeopleJSONAdapter.test.js | + +- Repo architecture: [`ai-docs/ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md) · Registry: [`ai-docs/SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) +- Coverage state & contracts baseline: `.sdd/manifest.json` diff --git a/src/components/ai-docs/components-spec.md b/src/components/ai-docs/components-spec.md index f02089b23..1d18e15e1 100644 --- a/src/components/ai-docs/components-spec.md +++ b/src/components/ai-docs/components-spec.md @@ -98,6 +98,7 @@ Full export list: `src/components/index.js`. | COMP-R-002 | `withAdapter` MUST call `adapter.connect()` before wrapping with `WebexDataProvider` | Prevents hooks reading disconnected adapter | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | none | PRESENT | | COMP-R-003 | `WebexDataProvider` MUST require adapter shape with five domain adapters | Matches adapter interface contract | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `src/components/WebexDataProvider/WebexDataProvider.test.js` | organizations adapter optional in propTypes | PRESENT | | COMP-R-004 | DOM class prefix MUST use `wxc` constant | Consistent theming/CSS | `src/constants.js` | `src/styles/_variables.scss` | none | PRESENT | +| COMP-R-005 | Exported component folders SHOULD follow co-located test and Storybook pattern | Regression safety for public API | `CONTRIBUTING.md` | `src/components/**/*.test.js`, `*.stories.js` | not every export has stories | PRESENT | | COMP-R-006 | All exported symbols MUST match `src/components/index.js` barrel | npm public API contract | `src/components/index.js` | build/export tests | none | PRESENT | | COMP-R-007 | Internal hooks under `hooks/` MUST subscribe/unsubscribe adapter observables safely | Prevents memory leaks on unmount | `src/components/hooks/useMeeting.js` | hook tests | none | PRESENT | | COMP-R-008 | `WebexDataProvider` PropTypes require five domain adapters (activities, meetings, memberships, people, rooms) | Documents minimum adapter shape for context | `src/components/WebexDataProvider/WebexDataProvider.jsx` | WebexDataProvider.test.js | organizationsAdapter on WebexJSONAdapter but not in PropTypes — host/SDK may still supply | PRESENT | @@ -108,6 +109,8 @@ Components follow a **container/presentation** split where data arrives exclusiv Hooks encapsulate RxJS subscription lifecycle for meeting, person, room, and activity streams. Generic components (`Button`, `Modal`) provide shared UX primitives with Webex styling. +Supporting folders (`icons/`, `inputs/`, `generic/`, `adaptive-cards/`) implement internal UI pieces. Only `Button` and `Modal` from `generic/` are npm-exported; other support components are consumed internally (see Class / Component Relationships). + ## Data Flow ```mermaid @@ -135,6 +138,12 @@ Data flows one way from adapter observables into hooks, then into React render o ## Sequence Diagram(s) +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| Adapter connect + meeting subscribe | below | `withAdapter` defers provider until connect completes | + ```mermaid sequenceDiagram participant Host @@ -180,6 +189,34 @@ classDiagram Relationship: HOC → Provider → feature components → hooks → adapter interfaces (external package). +**Internal components (not in public barrel):** + +| Component folder | Role | Used by | +|---|---|---| +| `WebexAdaptiveCard/` | Renders single adaptive card | `WebexAdaptiveCards` | +| `WebexAudioSettings/` | Audio device selection UI | `WebexSettings` | +| `WebexVideoSettings/` | Video device selection UI | `WebexSettings` | +| `WebexMeetingProvider/` | Provides meeting context | Meeting subtree | +| `WebexNoMedia/` | Placeholder when no media | Media components | + +**Hooks catalog** (`src/components/hooks/` — only `useMeetingDestination` exported from package barrel): + +| Hook | Purpose | Evidence | +|---|---|---| +| `useMeeting` | Subscribe to current meeting observables | `src/components/hooks/useMeeting.js` | +| `useMeetingControl` | Access meeting control instances | `src/components/hooks/useMeetingControl.js` | +| `useMeetingDestination` | Resolve meeting destination (exported) | `src/components/hooks/useMeetingDestination.js` | +| `usePerson` | Person data by ID | `src/components/hooks/usePerson.js` | +| `useMe` | Current user person data | `src/components/hooks/useMe.js` | +| `useRoom` | Room/space data | `src/components/hooks/useRoom.js` | +| `useMembers` | Membership roster | `src/components/hooks/useMembers.js` | +| `useActivity` | Single activity | `src/components/hooks/useActivity.js` | +| `useActivityStream` | Activity stream | `src/components/hooks/useActivityStream.js` | + +Additional hooks: `useActivityScroll`, `useOverflowActivities`, `useAdaptiveCard`, `useOrganization`, `useStream`, `useSpeakers`, `useMetrics`, `useElementDimensions`, `useElementPosition`, `useAutoFocus`, `useRef` — see `src/components/hooks/index.js`. + +Evidence: `src/components/` directory listing, import graph from `WebexMeeting.jsx`. + ## Use Cases | UC | Actor | Flow | Evidence | @@ -218,92 +255,57 @@ Component hooks subscribe to RxJS observables from adapter methods. Subscription Evidence: `src/components/hooks/useMeeting.js`, `src/adapters/MeetingsJSONAdapter.js`, `rxjs` peer dependency. -## Internal Components (supporting, not exported) - -| Component folder | Role | Used by | -|---|---|---| -| `WebexAdaptiveCard/` | Renders single adaptive card | `WebexAdaptiveCards` | -| `WebexAudioSettings/` | Audio device selection UI | `WebexSettings` | -| `WebexVideoSettings/` | Video device selection UI | `WebexSettings` | -| `WebexMeetingProvider/` | Provides meeting context | Meeting subtree | -| `WebexNoMedia/` | Placeholder when no media | Media components | -| `adaptive-cards/` | Adaptive card building blocks | Messaging/adaptive cards | -| `icons/` | SVG icon components | Generic and Webex components | -| `inputs/` | DateInput, Dropdown, TimeInput | Forms in components | -| `generic/` | Button, Modal, Badge, Spinner, … | Exported: Button, Modal; rest internal | - -Evidence: `src/components/` directory listing, import graph from `WebexMeeting.jsx`. - -## Hooks Catalog +## State Machine -| Hook | Purpose | Evidence | -|---|---|---| -| `useMeeting` | Subscribe to current meeting observables | `src/components/hooks/useMeeting.js` | -| `useMeetingControl` | Access meeting control instances | `src/components/hooks/useMeetingControl.js` | -| `useMeetingDestination` | Resolve meeting destination (exported) | `src/components/hooks/useMeetingDestination.js` | -| `usePerson` | Person data by ID | `src/components/hooks/usePerson.js` | -| `useMe` | Current user person data | `src/components/hooks/useMe.js` | -| `useRoom` | Room/space data | `src/components/hooks/useRoom.js` | -| `useMembers` | Membership roster | `src/components/hooks/useMembers.js` | -| `useActivity` | Single activity | `src/components/hooks/useActivity.js` | -| `useActivityStream` | Activity stream | `src/components/hooks/useActivityStream.js` | -| `useActivityScroll` | Scroll behavior for activities | `src/components/hooks/useActivityScroll.js` | -| `useOverflowActivities` | Overflow activity handling | `src/components/hooks/useOverflowActivities.js` | -| `useAdaptiveCard` | Adaptive card state | `src/components/hooks/useAdaptiveCard.js` | -| `useOrganization` | Organization data | `src/components/hooks/useOrganization.js` | -| `useStream` | Media stream helper | `src/components/hooks/useStream.js` | -| `useSpeakers` | Speaker list | `src/components/hooks/useSpeakers.js` | -| `useMetrics` | Metrics callback hook | `src/components/hooks/useMetrics.js` | -| `useElementDimensions` | Element size measurement | `src/components/hooks/useElementDimensions.js` | -| `useElementPosition` | Element position | `src/components/hooks/useElementPosition.js` | -| `useAutoFocus` | Focus management | `src/components/hooks/useAutoFocus.js` | -| `useRef` | Ref helper | `src/components/hooks/useRef.js` | +Meeting shell UI transitions follow adapter-reported `MeetingState` (from `@webex/component-adapter-interfaces`). `WebexMeeting` selects child components based on meeting status and auth requirements. -Barrel: `src/components/hooks/index.js`. Only `useMeetingDestination` exported from package barrel. +```mermaid +stateDiagram-v2 + [*] --> AuthOrWaiting: render WebexMeeting + AuthOrWaiting --> Interstitial: authenticated / guest flow complete + Interstitial --> InMeeting: join success + InMeeting --> Interstitial: leave / end + InMeeting --> SettingsOverlay: open settings + SettingsOverlay --> InMeeting: close settings +``` -## Module Conventions +Evidence: `src/components/WebexMeeting/WebexMeeting.jsx`, `@webex/component-adapter-interfaces` `MeetingState`. -- One folder per component: `ComponentName/ComponentName.jsx` + co-located `.scss`, `.test.js`, `.stories.js`. -- Use `webexComponentClasses()` from `src/components/helpers.js` for BEM-style class names. -- Responsive breakpoints from `src/components/breakpoints.js` (e.g. `PHONE_LARGE`). -- JSDoc on all public props and adapter-facing methods per `CONTRIBUTING.md`. +## Pitfalls -## Design Trade-offs +- Importing components without CSS yields unstyled UI — load `dist/css/webex-components.css`. +- Assuming `organizationsAdapter` on provider when only five adapters validated in propTypes — verify host adapter shape. +- Snapshot tests sensitive to class names — coordinate with styles module when changing prefix. +- `withAdapter` renders without provider until connect completes — do not assume adapter context on first paint. -- **Adapter injection vs bundled SDK:** Keeps package lightweight; hosts choose data source at cost of integration complexity. -- **JSON adapters in-repo:** Enables Storybook/offline demo without mocking entire SDK. -- **Side-effect style import:** `src/index.js` imports SCSS globally — simplifies consumer CSS at cost of bundler side-effect awareness. +Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. -## Host Integration +## Module Do's / Don'ts -Hosts MUST: +- DO: one folder per component with `ComponentName.jsx` + co-located `.scss`, `.test.js`, `.stories.js`. +- DO: use `webexComponentClasses()` from `src/components/helpers.js` for BEM-style class names. +- DO: use responsive breakpoints from `src/components/breakpoints.js` (e.g. `PHONE_LARGE`). +- DON'T: fetch Webex cloud data directly from components — use adapter hooks only. +- DON'T: export internal support components without updating `src/components/index.js` and `CONTRACTS.md`. -1. Install peer dependencies (`react`, `react-dom`, `rxjs`, `prop-types`). -2. Import compiled CSS from package `dist/css/`. -3. Provide adapter via `withAdapter` or manual `WebexDataProvider`. -4. For production Webex data, use SDK adapter from `@webex/sdk-component-adapter` (external repo) — not bundled here. - -Evidence: `README.md`, `package.json` peerDependencies. +Evidence: `CONTRIBUTING.md`, `src/components/helpers.js`. ## Export Stability -Public exports are semver-managed via semantic-release. Beta status documented in README — consumers should pin versions. +Public exports are semver-managed via semantic-release. Beta status documented in README — consumers should pin versions. Adding optional props is minor; removing exports or required props is major. Evidence: `README.md` Project Status, `package.json` release scripts. -## Error Handling & Failure Modes +## Host Integration & Theming -- Adapter not connected: `withAdapter` renders wrapped component without provider until connect completes. -- Missing meeting ID: components rely on adapter/state; hosts must pass valid IDs. -- Invalid URLs: use `isValidUrl` helper where links accepted. - -Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. +Hosts MUST: -## Pitfalls +1. Install peer dependencies (`react`, `react-dom`, `rxjs`, `prop-types`). +2. Import compiled CSS from package `dist/css/webex-components.css`. +3. Provide adapter via `withAdapter` or manual `WebexDataProvider`. +4. For production Webex data, use SDK adapter from `@webex/sdk-component-adapter` (external repo) — not bundled here. -- Importing components without CSS yields unstyled UI — load `dist/css/webex-components.css`. -- Assuming `organizationsAdapter` on provider when only five adapters validated in propTypes — verify host adapter shape. -- Snapshot tests sensitive to class names — coordinate with styles module when changing prefix. +Evidence: `README.md`, `package.json` peerDependencies. ## Test-Case Strategy (module) @@ -312,6 +314,12 @@ Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. - Hook tests with mock adapter context. - Storybook for visual states; Chromatic in CI. +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| COMP-R-002 connect-before-provider | `src/components/hoc/withAdapter.test.jsx` | none | +| COMP-R-005 co-located tests | `src/components/**/*.test.js` | not all exports have stories | +| COMP-R-007 hook cleanup | hook tests under `src/components/hooks/` | none | + Evidence: `src/components/**/*.test.js`, `.circleci/config.yml`. ## Traceability @@ -322,7 +330,10 @@ Evidence: `src/components/**/*.test.js`, `.circleci/config.yml`. | COMP-R-002 | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | | COMP-R-003 | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `src/components/WebexDataProvider/WebexDataProvider.test.js` | | COMP-R-004 | `src/constants.js` | style/component tests | -| COMP-R-005 | `src/components/index.js` | export barrel tests / build | +| COMP-R-005 | `src/components/` | `src/components/**/*.test.js` | | COMP-R-006 | `src/components/index.js` | build | | COMP-R-007 | `src/components/hooks/useMeeting.js` | hook tests | | COMP-R-008 | `src/components/WebexDataProvider/WebexDataProvider.jsx` | WebexDataProvider.test.js | + +- Repo architecture: [`ai-docs/ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md) · Registry: [`ai-docs/SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) +- Coverage state & contracts baseline: `.sdd/manifest.json` diff --git a/src/styles/ai-docs/styles-themes-spec.md b/src/styles/ai-docs/styles-themes-spec.md index c12000488..a3ec06a58 100644 --- a/src/styles/ai-docs/styles-themes-spec.md +++ b/src/styles/ai-docs/styles-themes-spec.md @@ -94,42 +94,10 @@ Internal Surface — SCSS partials are build-time, not direct public API. | STY-R-002 | Class prefix in SCSS MUST match JS constant `wxc` | Prevents broken selectors | `src/styles/_variables.scss`, `src/constants.js` | component tests | none | PRESENT | | STY-R-003 | Rollup MUST emit compressed CSS to `dist/css/webex-components.css` | Consumer import path | `rollup.config.js` | CI build | none | PRESENT | | STY-R-004 | Build MUST copy theme folders to `dist/themes/` | Host theme switching | `rollup.config.js` copy plugin | build artifact | none | PRESENT | +| STY-R-005 | Build MUST copy font files to `dist/assets/fonts/` | Hosts load bundled fonts | `rollup.config.js` copy plugin | CI build | none | PRESENT | | STY-R-006 | Theme SCSS files MUST be imported from `src/styles/index.scss` | Single CSS bundle | `src/styles/index.scss` | build | none | PRESENT | | STY-R-007 | Component styles aggregated via `src/components/_components.scss` | Component-level SCSS inclusion | `src/components/_components.scss` | build | none | PRESENT | -## SCSS File Registry - -| File | Role | -|---|---| -| `src/styles/index.scss` | Entry import graph | -| `src/styles/_variables.scss` | Prefix, fonts, meeting min dimensions | -| `src/styles/_colors.scss` | Color tokens | -| `src/styles/_mixins.scss` | Shared mixins | -| `src/styles/_fonts.scss` | Font face declarations | -| `src/styles/_defaults.scss` | Base element defaults | -| `src/themes/dark.scss` | Dark theme tokens | -| `src/themes/light.scss` | Light theme tokens | -| `src/components/_components.scss` | Imports all component SCSS partials | - -## Theme & Asset Assets - -| Path | Role | -|---|---| -| `src/themes/dark/webex-logo.svg` | Dark theme logo | -| `src/themes/light/webex-logo.svg` | Light theme logo | -| `src/assets/fonts/` | CiscoSansTT font files copied to `dist/assets/fonts/` | - -## Module Conventions - -- SCSS partials prefixed with `_` under `src/styles/`. -- Class prefix variable `$WEBEX_COMPONENTS_CLASS_PREFIX` must match `src/constants.js`. -- Component SCSS lives beside component JSX; registered through `_components.scss`. - -## Design Trade-offs - -- **Single compressed CSS bundle:** Simple host integration vs inability to tree-shake unused component styles. -- **Copy vs inline theme assets:** Theme folders copied verbatim for host static serving flexibility. - ## Design Overview SCSS is organized in layers: variables/mixins → color/font tokens → theme overrides → component aggregation. Themes (`dark.scss`, `light.scss`) set CSS custom properties or SCSS variables consumed by component rules. Rollup compiles a single CSS bundle for simplicity; theme folders ship as static assets for logos and theme-specific resources. @@ -157,6 +125,12 @@ flowchart LR ## Sequence Diagram(s) +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| Production build CSS + assets | below | SCSS `failOnError: true` aborts build on compile failure | + ```mermaid sequenceDiagram participant Dev as Developer @@ -187,17 +161,47 @@ Evidence: `src/components/helpers.js`, `src/styles/_variables.scss`. | UC-2 Theme assets | Host serves `dist/themes/dark` logos | `rollup.config.js` | | UC-3 Component author | Add SCSS beside new component; register in `_components.scss` | `src/components/` patterns | -## Host Integration +## SCSS File Registry -Hosts load compiled CSS once globally. Theme selection may swap CSS variables or load theme-specific assets from `dist/themes/`. Class prefix `wxc` must not be overridden without updating both JS and SCSS. +| File | Role | +|---|---| +| `src/styles/index.scss` | Entry import graph | +| `src/styles/_variables.scss` | Prefix, fonts, meeting min dimensions | +| `src/styles/_colors.scss` | Color tokens | +| `src/styles/_mixins.scss` | Shared mixins | +| `src/styles/_fonts.scss` | Font face declarations | +| `src/styles/_defaults.scss` | Base element defaults | +| `src/themes/dark.scss` | Dark theme tokens | +| `src/themes/light.scss` | Light theme tokens | +| `src/components/_components.scss` | Imports all component SCSS partials | + +## Theme & Asset Assets + +| Path | Role | +|---|---| +| `src/themes/dark/webex-logo.svg` | Dark theme logo | +| `src/themes/light/webex-logo.svg` | Light theme logo | +| `src/assets/fonts/` | CiscoSansTT font files copied to `dist/assets/fonts/` | + +Evidence: `rollup.config.js`, `src/assets/fonts/`. + +## Pitfalls -## Error Handling & Failure Modes +- Changing `$prefix` in SCSS without updating `WEBEX_COMPONENTS_CLASS_PREFIX` breaks tests and styles. +- SCSS compilation failures fail build (`failOnError: true` in Rollup scss plugin). +- Consumers who tree-shake JS but omit CSS see unstyled components. -- SCSS compilation failure aborts build (`failOnError: true` in Rollup scss plugin). -- Missing theme/font copy targets fail at build time via Rollup copy plugin. -- Host omitting CSS import results in unstyled components — see components spec pitfalls. +Evidence: `rollup.config.js`, `src/constants.js`. -Evidence: `rollup.config.js`. +## Module Do's / Don'ts + +- DO: prefix SCSS partials with `_` under `src/styles/`. +- DO: keep `$WEBEX_COMPONENTS_CLASS_PREFIX` aligned with `src/constants.js`. +- DO: register new component SCSS in `src/components/_components.scss`. +- DON'T: change `wxc-` class prefix in only JS or only SCSS. +- DON'T: expect per-component CSS tree-shaking from the single bundle model. + +Evidence: `src/styles/_variables.scss`, `src/constants.js`. ## Export Stability @@ -205,13 +209,11 @@ Published npm assets (`dist/css/`, `dist/themes/`, `dist/assets/`) follow semver Evidence: `package.json`, `README.md`. -## Pitfalls +## Host Integration & Theming -- Changing `$prefix` in SCSS without updating `WEBEX_COMPONENTS_CLASS_PREFIX` breaks tests and styles. -- SCSS compilation failures fail build (`failOnError: true` in Rollup scss plugin). -- Consumers who tree-shake JS but omit CSS see unstyled components. +Hosts load compiled CSS once globally. Theme selection may swap CSS variables or load theme-specific assets from `dist/themes/`. Class prefix `wxc` must not be overridden without updating both JS and SCSS. -Evidence: `rollup.config.js`, `src/constants.js`. +Evidence: `README.md`, `dist/css/webex-components.css`. ## Test-Case Strategy (module) @@ -219,6 +221,13 @@ Evidence: `rollup.config.js`, `src/constants.js`. - Build verification: `npm run build` produces expected dist artifacts. - Visual: Storybook/Chromatic for theme regressions. +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| STY-R-003 CSS bundle | CI build | none | +| STY-R-005 font copy | CI build / dist inspection | none | + +Evidence: `.circleci/config.yml`, `rollup.config.js`. + ## Traceability | Requirement | Code | Tests | @@ -228,3 +237,8 @@ Evidence: `rollup.config.js`, `src/constants.js`. | STY-R-003 | `rollup.config.js` | CI build | | STY-R-004 | `rollup.config.js` | CI build | | STY-R-005 | `rollup.config.js` | CI build | +| STY-R-006 | `src/styles/index.scss` | build | +| STY-R-007 | `src/components/_components.scss` | build | + +- Repo architecture: [`ai-docs/ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md) · Registry: [`ai-docs/SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) +- Coverage state & contracts baseline: `.sdd/manifest.json` From 11c6ca943fbd618a431d8fa6cb79cf11f5500e05 Mon Sep 17 00:00:00 2001 From: Akula Uday Date: Mon, 27 Jul 2026 19:34:12 +0530 Subject: [PATCH 4/4] =?UTF-8?q?docs(sdd):=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20metadata,=20withAdapter=20lifecycle,=20contracts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore template provenance blocks, fix withAdapter lifecycle docs, expand components Public Surface, restore REVIEW_CHECKLIST K1, add adapter error handling, sync validation metadata and CONTRACTS/ARCHITECTURE with code. Co-authored-by: Cursor --- .sdd/manifest.json | 18 +++---- AGENTS.md | 11 +++- ai-docs/ARCHITECTURE.md | 30 ++++++++++- ai-docs/CONTRACTS.md | 29 ++++++---- ai-docs/GETTING_STARTED.md | 9 ++++ ai-docs/GLOSSARY.md | 9 ++++ ai-docs/REVIEW_CHECKLIST.md | 54 ++++++++++++------- ai-docs/RULES.md | 9 ++++ ai-docs/SECURITY.md | 9 ++++ ai-docs/SPEC_INDEX.md | 9 ++++ ai-docs/patterns/co-located-tests.md | 9 ++++ ai-docs/patterns/with-adapter-injection.md | 19 ++++++- ai-docs/patterns/wxc-class-prefix.md | 9 ++++ src/adapters/ai-docs/adapters-spec.md | 29 +++++++++- src/components/ai-docs/components-spec.md | 61 +++++++++++++++++----- src/styles/ai-docs/styles-themes-spec.md | 11 +++- 16 files changed, 268 insertions(+), 57 deletions(-) diff --git a/.sdd/manifest.json b/.sdd/manifest.json index 3eae23828..22160feea 100644 --- a/.sdd/manifest.json +++ b/.sdd/manifest.json @@ -42,7 +42,7 @@ { "path": "src/components/", "coverage_status": "Specced", - "coverage_evidence": "96% field score assessed 2026-07-23; all exports, hooks catalog, internal components, HOCs, and conventions documented", + "coverage_evidence": "98% field score assessed 2026-07-27; all 31 barrel exports in Public Surface, hooks, HOCs, internal components, and conventions documented", "canonical_spec": "src/components/ai-docs/components-spec.md", "contracts": { "provides": [ @@ -101,7 +101,7 @@ "enforces_domain_rules": false, "is_concurrent_async": true, "owns_persistence": false, - "returns_caller_errors": false, + "returns_caller_errors": true, "has_design_tradeoff": false, "stateful_transitions": true, "exposes_wire_protocol": false, @@ -185,18 +185,18 @@ "generator_runtime": "cursor-agent-session", "generator_model": "composer", "generator_runtime_source": "host-metadata", - "validator_runtime": null, - "validator_model": null, - "validator_runtime_source": null, + "validator_runtime": "codex-agent-session", + "validator_model": "gpt-5", + "validator_runtime_source": "host-metadata", "minimum_independence": "different-runtime", - "runtime_fallback_tier": null, + "runtime_fallback_tier": "different-runtime", "blocking_severities": ["Blocking"], "generator_run_id": "bootstrap-2026-07-23T124500Z", - "validator_run_id": null, - "source_commit": null, + "validator_run_id": "validation-2026-07-23T085321Z", + "source_commit": "c0140453d4137b7b78bb5656bd13c3e13215d82b", "base_ref": "master", "head_ref": "SDLC_SKILLS_FOR_COMPONENTS", - "status": "not-run" + "status": "pass-with-warnings" }, "layout": { "sdd_root": ".sdd", diff --git a/AGENTS.md b/AGENTS.md index bcd153816..305c6aa39 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,12 @@ + + # AGENTS.md — webex/components > You are the agent entry point — read first. Next: router [`SPEC_INDEX.md`](ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ai-docs/ARCHITECTURE.md). Load this + `SPEC_INDEX.md` first; pull module/standing docs on demand. @@ -71,7 +80,7 @@ src/ ## Common Gotchas - **Styles side effect:** `src/index.js` imports `./styles/index.scss` — consumers must load compiled CSS from `dist/css/webex-components.css` or equivalent. -- **Adapter connect lifecycle:** `withAdapter` renders children only after `adapter.connect()` resolves; components may briefly render without adapter context. +- **Adapter connect lifecycle:** `withAdapter` renders the wrapped component immediately; while `adapter.connect()` is pending, it returns the component **without** `WebexDataProvider` and passes `adapterConnected={false}`. After connect resolves, it re-renders with `WebexDataProvider` and `adapterConnected={true}`. Do not assume `AdapterContext` is available on first paint. - **JSON adapter datasource shape:** `WebexJSONAdapter` expects top-level keys `activities`, `meetings`, `memberships`, `organizations`, `people`, `rooms`. - **Semantic release:** Version bumps are automated — do not manually edit version in `package.json` for releases. diff --git a/ai-docs/ARCHITECTURE.md b/ai-docs/ARCHITECTURE.md index 237b43260..9f102e3fc 100644 --- a/ai-docs/ARCHITECTURE.md +++ b/ai-docs/ARCHITECTURE.md @@ -1,3 +1,12 @@ + + # ARCHITECTURE — webex/components > Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). Per-module detail in manifest-routed module specs. @@ -133,13 +142,30 @@ Evidence: `README.md`, `src/components/hoc/withAdapter.jsx`, `package.json` peer - **Published artifact:** `@webex/components` npm package consumed by host applications and widgets. - **SDD tooling source:** `SDLC-Skills` plugin installed locally (not a runtime dependency). -Evidence: `README.md`, `package.json`, `.sdd/metrics/source.env`. +Evidence: `README.md`, `package.json`. ## Testing & Quality - Jest unit/snapshot tests co-located with components and adapters. - Storybook documents components at https://webex.github.io/components/storybook. -- CircleCI: lint → test:coverage → chromatic → build. +- CircleCI workflow `test_and_storybook` (see `.circleci/config.yml`): + - `install` → `lint` and `unit_test` run in parallel (both require install). + - `storybook_preview` (Chromatic via `npm run chromatic`) runs after `unit_test` on **non-master** branches only. + - `build` runs after both `lint` and `unit_test` on **master** only. + +```mermaid +flowchart TB + install[install] + lint[lint] + unitTest[unit_test] + chromatic[storybook_preview] + build[build] + install --> lint + install --> unitTest + unitTest --> chromatic + lint --> build + unitTest --> build +``` Evidence: `.circleci/config.yml`, `package.json` scripts. diff --git a/ai-docs/CONTRACTS.md b/ai-docs/CONTRACTS.md index 832169f8e..6518c21cd 100644 --- a/ai-docs/CONTRACTS.md +++ b/ai-docs/CONTRACTS.md @@ -1,3 +1,12 @@ + + # Contracts Catalog — webex/components > Machine source: `.sdd/manifest.json`. Package entry: `src/index.js` (Rollup input). Component barrel: `src/components/index.js`. Adapter module barrel: `src/adapters/index.js` (internal — only `WebexJSONAdapter` is re-exported from the package root). @@ -41,6 +50,7 @@ | pkg.MeetingContext | components | MeetingContext | React context for meeting scope | Semver beta | `src/components/index.js` | | pkg.css.bundle | styles-themes | webex-components.css | Compiled stylesheet | Semver beta | `dist/css/webex-components.css` | | pkg.themes | styles-themes | dist/themes/* | Theme asset folders | Semver beta | `rollup.config.js` | +| pkg.fonts | styles-themes | dist/assets/fonts/* | Font files (CiscoSansTT) | Semver beta | `rollup.config.js` | ### Internal adapter classes (not npm package exports) @@ -71,15 +81,16 @@ Evidence: folder listing under `src/components/` vs `src/components/index.js` ex ## Requires — what this repo depends on -| Dependency | What is consumed | Availability | Version floor | -|---|---|---|---| -| `@webex/component-adapter-interfaces` | Adapter base classes, MeetingState | npm | ^1.28.0 | -| `react` / `react-dom` | UI | peer | 18.3.1 | -| `rxjs` | Observables | peer | ^6.6.2 | -| `prop-types` | Runtime validation | peer | ^15.7.2 | -| `@babel/runtime` | Transpiled helpers | peer | ^7.11.2 | -| `adaptivecards-templating` | Adaptive card templates | bundled dep | ^2.2.0 | -| `markdown-it` | Markdown in messaging | bundled dep | ^12.3.2 | +| Dependency (service / package / datastore) | What is consumed | Schema / detail link | Availability assumption | Fallback on failure | Version floor | +|---|---|---|---|---|---| +| `@webex/component-adapter-interfaces` | Adapter base classes, `MeetingState`, domain adapter contracts | npm package + `src/adapters/ai-docs/adapters-spec.md` | Host supplies a connected adapter instance at runtime | Components cannot load Webex data; host must inject SDK or JSON adapter | ^1.28.0 | +| `react` / `react-dom` | UI rendering | `package.json` peerDependencies | Required at install/build time | Build or runtime failure if missing | 18.3.1 | +| `rxjs` | Observable streams from adapters | `package.json` peerDependencies | Required at install/build time | Adapter hooks cannot subscribe | ^6.6.2 | +| `prop-types` | Runtime prop validation | `package.json` peerDependencies | Required at install/build time | Dev-time PropTypes warnings only | ^15.7.2 | +| `@babel/runtime` | Transpiled helper functions | `package.json` peerDependencies | Required at install/build time | Runtime errors in transpiled code | ^7.11.2 | +| `adaptivecards-templating` | Adaptive card template expansion | `package.json` dependencies | Bundled with package | Adaptive card UI degrades or fails render | ^2.2.0 | +| `markdown-it` | Markdown rendering in messaging | `package.json` dependencies | Bundled with package | Messaging markdown falls back to plain text or fails render | ^12.3.2 | +| Host adapter (production) | Live Webex data via `@webex/sdk-component-adapter` (external) | `README.md`, `@webex/component-adapter-interfaces` | Host-managed Webex SDK session | Use `WebexJSONAdapter` for offline/demo only | n/a | ## Compatibility & Deprecation Policy diff --git a/ai-docs/GETTING_STARTED.md b/ai-docs/GETTING_STARTED.md index f2cb54306..daf2b3d6a 100644 --- a/ai-docs/GETTING_STARTED.md +++ b/ai-docs/GETTING_STARTED.md @@ -1,3 +1,12 @@ + + # Getting Started — webex/components > Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). diff --git a/ai-docs/GLOSSARY.md b/ai-docs/GLOSSARY.md index 32fe56d64..b1ceee5a5 100644 --- a/ai-docs/GLOSSARY.md +++ b/ai-docs/GLOSSARY.md @@ -1,3 +1,12 @@ + + # Glossary — webex/components > Start here → root [`AGENTS.md`](../AGENTS.md) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). diff --git a/ai-docs/REVIEW_CHECKLIST.md b/ai-docs/REVIEW_CHECKLIST.md index 692530f73..0ca35cea3 100644 --- a/ai-docs/REVIEW_CHECKLIST.md +++ b/ai-docs/REVIEW_CHECKLIST.md @@ -1,41 +1,55 @@ + + # Review-Check Catalog — webex/components -> Draft checklist for SDD changes. Validator runs independently (Session B). +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc at Review & Merge. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +> Each finding records: severity (Blocking / Important / Medium / Minor), check id, file path, what's wrong, +> why it matters, a concrete fix. Any Blocking finding fails the gate. ## Core checks (always run) | # | Check | What it verifies | Severity if it fails | |---|---|---|---| -| C1 | Spec-currency + WHAT/WHY | Spec/docs updated in same PR as code; requirements have WHAT and WHY | Blocking | -| C2 | Contract correctness | Export/prop changes reflected in `CONTRACTS.md` and module specs | Blocking | -| C3 | Code-vs-spec match | Exports in `src/components/index.js` / `src/adapters/index.js` match specs | Blocking | -| C4 | Test adequacy | Jest tests for changed behavior; snapshots updated deliberately | Important | -| C5 | Error handling + input validation | Adapter connect lifecycle; URL validation where applicable | Important | -| C6 | Security baseline | No secrets; no credential logging per `SECURITY.md` | Blocking | +| C1 | Spec-currency + WHAT/WHY | Spec/docs changed in the same change as code; the implementation plan's repo-specific AI Docs Impact matrix entries are complete and closed; every requirement (incl. ADDED) states WHAT and WHY | Blocking | +| C2 | Contract correctness | Provides/Requires delta is real and complete; no undocumented breaking change to a public surface | Blocking | +| C3 | Code-vs-spec match | Signatures, data-flow, and architecture claims in the spec match the actual code (file path) | Blocking | +| C4 | Test adequacy | Each acceptance criterion has a test with a positive AND a negative case; changed-line coverage meets the bar | Important | +| C5 | Error handling + input validation | Untrusted input validated at boundaries; failure/edge paths handled, not swallowed | Important | +| C6 | Security baseline | No hardcoded secrets; authz enforced; data-classification/logging rules respected (per `SECURITY.md`) | Blocking | -## Coverage-conditional checks +## Coverage-conditional checks (run by the touched module's manifest coverage state) | # | Check | When it applies | What it verifies | Severity | |---|---|---|---|---| -| K1 | Regression guard | Changing adapter observables or public props | Existing tests still pass; snapshots reviewed | Important | -| K2 | Grounding | Any module | Claims cite file paths from real source | Important | -| K3 | Drift threshold | Tracked modules | Spec matches current exports | Important | -| K4 | Coverage-state accuracy | Manifest promotion | Coverage score evidence matches spec completeness | Medium | +| K1 | Regression guard | Modifying a weakly covered module, or any MODIFIED/REMOVED requirement | A characterization baseline exists; invariants the change claims NOT to alter still hold (positive + negative) | Blocking | +| K2 | Grounding | Weakly covered module | Claims cite real code (file path), not memory; uncovered public surfaces flagged `[NEEDS HUMAN INPUT]` | Important | +| K3 | Drift threshold | Any tracked module | Module drift is within its status threshold (see `RULES.md` / `coverage-policy.defaults.yaml`) | Important | +| K4 | Coverage-state accuracy | Coverage-state change proposed | The recorded manifest coverage state matches the evidence; promotion/demotion rules honored | Medium | -## Cross-cutting checks +## Cross-cutting checks (apply at higher risk / autonomy) | # | Check | What it verifies | Severity | |---|---|---|---| -| X1 | Cross-model review | spec-validator uses different runtime than generator | Blocking when required | -| X2 | Observability | No PII/token logging added | Medium | -| X3 | Rollout safety | Beta breaking changes documented in CHANGELOG/README | Important | +| X1 | Cross-model review | The artifact was validated by a different runtime than the one that generated it (generator ≠ validator) | Blocking when required | +| X2 | Observability | Logs/metrics/alerts adequate for the change; nothing sensitive logged | Medium | +| X3 | Rollout safety | Feature-flag default is safe; rollback path exists; migration/rollout interlock is correct | Important | ## How the set is selected -1. Always run core checks C1–C6. -2. Add K1–K4 when touching Partial/Specced modules. -3. Add X1 for SDD bootstrap validation handoff. +1. Always run the 6 core checks. +2. Add the coverage-conditional checks whose "when it applies" matches the touched modules' manifest coverage state. +3. Add the cross-cutting checks when the change is high-risk or runs at higher autonomy. ## Output -- Compliance matrix + severity-sorted findings + verdict (Pass / Pass-with-warnings / Blocked). Draft only. +- A compliance matrix + severity-sorted findings + a verdict (Pass / Pass-with-warnings / Blocked). + Draft only; a human posts. diff --git a/ai-docs/RULES.md b/ai-docs/RULES.md index cf568a2ce..bcd67a924 100644 --- a/ai-docs/RULES.md +++ b/ai-docs/RULES.md @@ -1,3 +1,12 @@ + + # Rules — webex/components > Start here → root [`AGENTS.md`](../AGENTS.md) · critical rules live in AGENTS.md. diff --git a/ai-docs/SECURITY.md b/ai-docs/SECURITY.md index f8c7b12a8..2b09ba9fa 100644 --- a/ai-docs/SECURITY.md +++ b/ai-docs/SECURITY.md @@ -1,3 +1,12 @@ + + # Security Baseline — webex/components > Client-side React library — hosts own authentication and token storage. diff --git a/ai-docs/SPEC_INDEX.md b/ai-docs/SPEC_INDEX.md index 6b340c753..9e4e0a823 100644 --- a/ai-docs/SPEC_INDEX.md +++ b/ai-docs/SPEC_INDEX.md @@ -1,3 +1,12 @@ + + # Spec Index — webex/components > Start here → root [`AGENTS.md`](../AGENTS.md). **Source of truth:** `.sdd/manifest.json` (this file mirrors it). diff --git a/ai-docs/patterns/co-located-tests.md b/ai-docs/patterns/co-located-tests.md index 8b2ac5c21..1803c30f3 100644 --- a/ai-docs/patterns/co-located-tests.md +++ b/ai-docs/patterns/co-located-tests.md @@ -1,3 +1,12 @@ + + # Pattern: co-located component tests > Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`RULES.md`](../RULES.md) diff --git a/ai-docs/patterns/with-adapter-injection.md b/ai-docs/patterns/with-adapter-injection.md index ee35ba56b..5adc131a9 100644 --- a/ai-docs/patterns/with-adapter-injection.md +++ b/ai-docs/patterns/with-adapter-injection.md @@ -1,3 +1,12 @@ + + # Pattern: adapter injection via withAdapter > Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · components spec @@ -19,11 +28,17 @@ const Enhanced = withAdapter(MyComponent, adapterFactory); ## Incorrect ```javascript -// Render WebexMeeting without adapter context or before connect() completes +// Render WebexMeeting without withAdapter/WebexDataProvider — no AdapterContext + +// Or: read AdapterContext inside withAdapter-wrapped component without checking adapterConnected +function MyView() { + const adapter = useContext(AdapterContext); // undefined on first paint + return adapter.meetingsAdapter.getMeetingInfo(...); +} ``` -**Why wrong:** Hooks read `AdapterContext`; missing or disconnected adapter yields empty/error state. +**Why wrong:** Hooks and context consumers expect `AdapterContext` from `WebexDataProvider`. `withAdapter` renders the wrapped component before connect completes; without the provider (or without checking `adapterConnected`), adapter access fails. ## Where it appears diff --git a/ai-docs/patterns/wxc-class-prefix.md b/ai-docs/patterns/wxc-class-prefix.md index c0d50fcb3..0edc8054d 100644 --- a/ai-docs/patterns/wxc-class-prefix.md +++ b/ai-docs/patterns/wxc-class-prefix.md @@ -1,3 +1,12 @@ + + # Pattern: wxc class prefix > Router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`GLOSSARY.md`](../GLOSSARY.md) diff --git a/src/adapters/ai-docs/adapters-spec.md b/src/adapters/ai-docs/adapters-spec.md index 426bba5c2..c11e6bd0e 100644 --- a/src/adapters/ai-docs/adapters-spec.md +++ b/src/adapters/ai-docs/adapters-spec.md @@ -1,3 +1,12 @@ + + # adapters — SPEC > Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). @@ -12,7 +21,7 @@ | Coverage score | 97% assessed 2026-07-23 — all domain adapters and meeting controls documented | | Generated from | `module-spec` @ SDLC template library `0.2.1` | | generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | -| Validation status | not-run | +| Validation status | pass-with-warnings, validator codex-agent-session, assessed 2026-07-23 (0 blocking) | ## Evidence Rules @@ -209,6 +218,24 @@ Meeting updates propagate via RxJS `Observable` streams. Multiple subscribers (h Evidence: `src/adapters/MeetingsJSONAdapter.js`, rxjs imports. +## Error Handling & Failure Modes + +JSON adapters surface invalid inputs and missing records through RxJS `observer.error(...)` on subscribed observables. Callers (hooks, tests, Storybook) MUST handle error emissions — errors are not swallowed or converted to silent empty results. + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| Unknown meeting ID | `observer.error(Error('Could not find meeting with ID "…"'))` | Subscribe with error handler; show fallback UI | +| Unknown person ID or search miss | `observer.error(Error('Could not find person…'))` | Guard UI before subscribe; handle error in hook | +| Unknown room ID | `observer.error(Error('Could not find room with ID "…"'))` | Show not-found state; avoid assuming room exists | +| Unknown activity ID | `observer.error(Error('Could not find activity with ID "…"'))` | Handle in activity stream subscription | +| Unknown organization ID | `observer.error(Error('Could not find any organization with ID "…"'))` | Handle in organization lookup | +| Invalid membership destination | `observer.error(Error('Could not find members for destination "…"'))` | Validate destination before subscribe | +| Room creation failure | `observer.error(Error('error in creating room'))` | Retry or surface error to host | +| Control preconditions not met (switch camera/mic/speaker) | `observer.error(Error('Could not find meeting with ID "…" to add … control'))` | Guard control UI until meeting exists | +| Activity attachment/post failure | `observer.error(Error('Unable to create/post…'))` | Show error toast; do not assume success | + +Evidence: `src/adapters/MeetingsJSONAdapter.js`, `src/adapters/PeopleJSONAdapter.js`, `src/adapters/RoomsJSONAdapter.js`, `src/adapters/ActivitiesJSONAdapter.js`, `src/adapters/MembershipJSONAdapter.js`, `src/adapters/OrganizationsJSONAdapter.js`, `src/adapters/MeetingsJSONAdapter/controls/*.js`. + ## State Machine Meeting status transitions include `NOT_JOINED` and joined/in-meeting states aligned with `MeetingState` from `@webex/component-adapter-interfaces`. Controls gate transitions (e.g. join, leave). diff --git a/src/components/ai-docs/components-spec.md b/src/components/ai-docs/components-spec.md index 1d18e15e1..17af3f636 100644 --- a/src/components/ai-docs/components-spec.md +++ b/src/components/ai-docs/components-spec.md @@ -1,3 +1,12 @@ + + # components — SPEC > Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). @@ -9,10 +18,10 @@ | Module id | components | | Source path(s) | `src/components/`, `src/constants.js`, `src/util.js` | | Doc kind | Module spec | -| Coverage score | 96% assessed 2026-07-23 — all exports, hooks, HOCs, and internal support folders documented | +| Coverage score | 98% assessed 2026-07-27 — all 31 barrel exports documented in Public Surface; hooks, HOCs, contexts, and internal support folders documented | | Generated from | `module-spec` @ SDLC template library `0.2.1` | | generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | -| Validation status | not-run | +| Validation status | pass-with-warnings, validator codex-agent-session, assessed 2026-07-23 (0 blocking) | ## Evidence Rules @@ -76,12 +85,39 @@ src/components/ | Contract ID | Type | Surface | Purpose | Compatibility | Schema / detail link | Root index | |---|---|---|---|---|---|---| -| components.WebexMeeting | SDK export | `WebexMeeting` component | Default meeting UX | Semver beta | `src/components/WebexMeeting/WebexMeeting.jsx` | `ai-docs/CONTRACTS.md` | -| components.withAdapter | SDK export | HOC factory | Inject adapter + connect lifecycle | Semver | `src/components/hoc/withAdapter.jsx` | `ai-docs/CONTRACTS.md` | -| components.WebexDataProvider | SDK export | Context provider | Pass adapter to subtree | Semver | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `ai-docs/CONTRACTS.md` | -| components.useMeetingDestination | SDK export | Hook | Resolve meeting destination | Semver | `src/components/hooks/useMeetingDestination.js` | `ai-docs/CONTRACTS.md` | - -Full export list: `src/components/index.js`. +| components.WebexAvatar | SDK export | `WebexAvatar` component | Avatar display | Semver beta | `src/components/WebexAvatar/WebexAvatar.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexActivity | SDK export | `WebexActivity` component | Single activity | Semver beta | `src/components/WebexActivity/WebexActivity.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexActivityStream | SDK export | `WebexActivityStream` component | Activity stream | Semver beta | `src/components/WebexActivityStream/WebexActivityStream.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexAdaptiveCards | SDK export | `WebexAdaptiveCards` component | Adaptive cards container | Semver beta | `src/components/WebexAdaptiveCards/WebexAdaptiveCards.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexDataProvider | SDK export | `WebexDataProvider` component | Adapter context provider | Semver beta | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexInMeeting | SDK export | `WebexInMeeting` component | In-meeting layout | Semver beta | `src/components/WebexInMeeting/WebexInMeeting.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexInterstitialMeeting | SDK export | `WebexInterstitialMeeting` component | Pre-join interstitial | Semver beta | `src/components/WebexInterstitialMeeting/WebexInterstitialMeeting.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexLocalMedia | SDK export | `WebexLocalMedia` component | Local media display | Semver beta | `src/components/WebexLocalMedia/WebexLocalMedia.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMediaAccess | SDK export | `WebexMediaAccess` component | Media permission prompt | Semver beta | `src/components/WebexMediaAccess/WebexMediaAccess.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeeting | SDK export | `WebexMeeting` component | Full meeting shell | Semver beta | `src/components/WebexMeeting/WebexMeeting.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeetingGuestAuthentication | SDK export | `WebexMeetingGuestAuthentication` component | Guest auth UI | Semver beta | `src/components/WebexMeetingGuestAuthentication/WebexMeetingGuestAuthentication.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeetingHostAuthentication | SDK export | `WebexMeetingHostAuthentication` component | Host auth UI | Semver beta | `src/components/WebexMeetingHostAuthentication/WebexMeetingHostAuthentication.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeetingControl | SDK export | `WebexMeetingControl` component | Single meeting control | Semver beta | `src/components/WebexMeetingControl/WebexMeetingControl.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeetingControlBar | SDK export | `WebexMeetingControlBar` component | Control bar | Semver beta | `src/components/WebexMeetingControlBar/WebexMeetingControlBar.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMeetingInfo | SDK export | `WebexMeetingInfo` component | Meeting info display | Semver beta | `src/components/WebexMeetingInfo/WebexMeetingInfo.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMember | SDK export | `WebexMember` component | Single member row | Semver beta | `src/components/WebexMember/WebexMember.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMemberRoster | SDK export | `WebexMemberRoster` component | Member roster | Semver beta | `src/components/WebexMemberRoster/WebexMemberRoster.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexMessaging | SDK export | `WebexMessaging` component | Messaging UI | Semver beta | `src/components/WebexMessaging/WebexMessaging.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexRemoteMedia | SDK export | `WebexRemoteMedia` component | Remote media tiles | Semver beta | `src/components/WebexRemoteMedia/WebexRemoteMedia.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexSettings | SDK export | `WebexSettings` component | Settings panel | Semver beta | `src/components/WebexSettings/WebexSettings.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexWaitingForHost | SDK export | `WebexWaitingForHost` component | Waiting state UI | Semver beta | `src/components/WebexWaitingForHost/WebexWaitingForHost.jsx` | `ai-docs/CONTRACTS.md` | +| components.SignIn | SDK export | `SignIn` component | Sign-in UI | Semver beta | `src/components/SignIn/SignIn.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexSearchPeople | SDK export | `WebexSearchPeople` component | People search UI | Semver beta | `src/components/WebexSearchPeople/WebexSearchPeople.jsx` | `ai-docs/CONTRACTS.md` | +| components.WebexCreateSpace | SDK export | `WebexCreateSpace` component | Create space UI | Semver beta | `src/components/WebexCreateSpace/WebexCreateSpace.jsx` | `ai-docs/CONTRACTS.md` | +| components.useMeetingDestination | SDK export | `useMeetingDestination` hook | Resolve meeting destination | Semver beta | `src/components/hooks/useMeetingDestination.js` | `ai-docs/CONTRACTS.md` | +| components.withMeeting | SDK export | `withMeeting` HOC | Meeting scope wrapper | Semver beta | `src/components/hoc/withMeeting.jsx` | `ai-docs/CONTRACTS.md` | +| components.withAdapter | SDK export | `withAdapter` HOC | Adapter lifecycle injection | Semver beta | `src/components/hoc/withAdapter.jsx` | `ai-docs/CONTRACTS.md` | +| components.Button | SDK export | `Button` component | Generic button | Semver beta | `src/components/generic/Button/Button.jsx` | `ai-docs/CONTRACTS.md` | +| components.Modal | SDK export | `Modal` component | Generic modal | Semver beta | `src/components/generic/Modal/Modal.jsx` | `ai-docs/CONTRACTS.md` | +| components.AdapterContext | SDK export | `AdapterContext` context | React context for adapter | Semver beta | `src/components/hooks/contexts.js` | `ai-docs/CONTRACTS.md` | +| components.MeetingContext | SDK export | `MeetingContext` context | React context for meeting scope | Semver beta | `src/components/hooks/contexts.js` | `ai-docs/CONTRACTS.md` | + +Evidence: `src/components/index.js` (31 barrel exports). ## Requires (dependencies) @@ -95,7 +131,7 @@ Full export list: `src/components/index.js`. | ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | |---|---|---|---|---|---|---| | COMP-R-001 | Exported components MUST be listed in `src/components/index.js` | Defines npm public API | `src/components/index.js` | `package.json` exports via Rollup entry | none | PRESENT | -| COMP-R-002 | `withAdapter` MUST call `adapter.connect()` before wrapping with `WebexDataProvider` | Prevents hooks reading disconnected adapter | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | none | PRESENT | +| COMP-R-002 | `withAdapter` MUST NOT wrap with `WebexDataProvider` until `adapter.connect()` resolves; wrapped component may render without context meanwhile | Prevents hooks from reading adapter before connect; allows loading UI via `adapterConnected` prop | `src/components/hoc/withAdapter.jsx` | `src/components/hoc/withAdapter.test.jsx` | none | PRESENT | | COMP-R-003 | `WebexDataProvider` MUST require adapter shape with five domain adapters | Matches adapter interface contract | `src/components/WebexDataProvider/WebexDataProvider.jsx` | `src/components/WebexDataProvider/WebexDataProvider.test.js` | organizations adapter optional in propTypes | PRESENT | | COMP-R-004 | DOM class prefix MUST use `wxc` constant | Consistent theming/CSS | `src/constants.js` | `src/styles/_variables.scss` | none | PRESENT | | COMP-R-005 | Exported component folders SHOULD follow co-located test and Storybook pattern | Regression safety for public API | `CONTRIBUTING.md` | `src/components/**/*.test.js`, `*.stories.js` | not every export has stories | PRESENT | @@ -142,7 +178,7 @@ Sequence coverage: | Operation group | Diagram | Failure / recovery coverage | |---|---|---| -| Adapter connect + meeting subscribe | below | `withAdapter` defers provider until connect completes | +| Adapter connect + meeting subscribe | below | first paint without provider; provider added after connect | ```mermaid sequenceDiagram @@ -154,10 +190,11 @@ sequenceDiagram Host->>WithAdapter: render with props WithAdapter->>Adapter: adapterFactory(props) + WithAdapter->>Comp: render without provider (adapterConnected=false) WithAdapter->>Adapter: connect() Adapter-->>WithAdapter: connected WithAdapter->>Provider: adapter - Provider->>Comp: AdapterContext available + Provider->>Comp: AdapterContext available (adapterConnected=true) Comp->>Adapter: subscribe meeting observables Adapter-->>Comp: meeting state updates ``` @@ -276,7 +313,7 @@ Evidence: `src/components/WebexMeeting/WebexMeeting.jsx`, `@webex/component-adap - Importing components without CSS yields unstyled UI — load `dist/css/webex-components.css`. - Assuming `organizationsAdapter` on provider when only five adapters validated in propTypes — verify host adapter shape. - Snapshot tests sensitive to class names — coordinate with styles module when changing prefix. -- `withAdapter` renders without provider until connect completes — do not assume adapter context on first paint. +- `withAdapter` renders the wrapped component immediately without `WebexDataProvider` while connect is pending — use `adapterConnected` prop for loading UI; do not assume `AdapterContext` on first paint. Evidence: `src/components/hoc/withAdapter.jsx`, `src/util.js`. diff --git a/src/styles/ai-docs/styles-themes-spec.md b/src/styles/ai-docs/styles-themes-spec.md index a3ec06a58..884f71298 100644 --- a/src/styles/ai-docs/styles-themes-spec.md +++ b/src/styles/ai-docs/styles-themes-spec.md @@ -1,3 +1,12 @@ + + # styles-themes — SPEC > Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). @@ -12,7 +21,7 @@ | Coverage score | 95% assessed 2026-07-23 — SCSS graph, themes, fonts, and build outputs documented | | Generated from | `module-spec` @ SDLC template library `0.2.1` | | generated_by / approved_by / updated_at | cursor-agent-session / pending PR approval / 2026-07-23 | -| Validation status | not-run | +| Validation status | pass-with-warnings, validator codex-agent-session, assessed 2026-07-23 (0 blocking) | ## Evidence Rules