Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 37 additions & 12 deletions ai-docs/RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,72 @@

Coverage state is mirrored from `.sdd/manifest.json`. Every module is currently `DRAFT`: specs were generated fresh during the SDLC migration (2026-06-29) and have not been validated. **Cross-check code before relying on any spec claim** (drift tolerance ≤ 25%, see below).

| Module | Manifest coverage state | What it means here |
|---|---|---|
| `store` (`@webex/cc-store`, `packages/contact-center/store`) | DRAFT | Tier-1, sole SDK access point (`store.ts` `getInstance`). Spec `store/ai-docs/store-spec.md` is unvalidated — verify observables/SDK proxying against `store/src/store.ts` and `store/src/storeEventsWrapper.ts`. |
| `cc-components` (`@webex/cc-components`, `packages/contact-center/cc-components`) | DRAFT | Tier-1 presentational primitives. Verify prop contracts against `cc-components/src/` before trusting the spec. |
| `cc-widgets` (`@webex/cc-widgets`, `packages/contact-center/cc-widgets`) | DRAFT | Tier-1 r2wc aggregator. The custom-element registry lives in `cc-widgets/src/wc.ts` — cross-check element names/attrs there. |
| `cc-digital-channels` (`@webex/cc-digital-channels`, `packages/contact-center/cc-digital-channels`) | DRAFT | Tier-1 digital-channels widget (`widget-cc-digital-channels`). Verify against `cc-digital-channels/src/`. |
| `station-login` (`@webex/cc-station-login`, `packages/contact-center/station-login`) | DRAFT | Tier-1 widget. Verify against `station-login/src/`. |
| `user-state` (`@webex/cc-user-state`, `packages/contact-center/user-state`) | DRAFT | Tier-1 widget. Verify state/idle-code logic against `user-state/src/helper.ts`. |
| `task` (`@webex/cc-task`, `packages/contact-center/task`) | DRAFT | Tier-1 bundle of sub-widgets CallControl, CallControlCAD, IncomingTask, OutdialCall, TaskList. Verify per-widget behavior against `task/src/{Widget}/index.tsx` and `task/src/helper.ts`. |
| `ui-logging` (`@webex/cc-ui-logging`, `packages/contact-center/ui-logging`) | DRAFT | Tier-2 telemetry. `withMetrics` HOC + `metricsLogger` in `ui-logging/src/`. |
| `test-fixtures` (`@webex/test-fixtures`, `packages/contact-center/test-fixtures`) | DRAFT | Tier-2 shared mocks/helpers in `test-fixtures/src/`. |
| `meetings-widgets` (`@webex/widgets`, `packages/@webex/widgets`) | DRAFT | Tier-2 legacy meetings family, separate from the CC widget family. Out of scope for CC rules below unless explicitly named. |
| Module | Manifest coverage state | What it means here |
| --------------------------------------------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `store` (`@webex/cc-store`, `packages/contact-center/store`) | DRAFT | Tier-1, sole SDK access point (`store.ts` `getInstance`). Spec `store/ai-docs/store-spec.md` is unvalidated — verify observables/SDK proxying against `store/src/store.ts` and `store/src/storeEventsWrapper.ts`. |
| `cc-components` (`@webex/cc-components`, `packages/contact-center/cc-components`) | DRAFT | Tier-1 presentational primitives. Verify prop contracts against `cc-components/src/` before trusting the spec. |
| `cc-widgets` (`@webex/cc-widgets`, `packages/contact-center/cc-widgets`) | DRAFT | Tier-1 r2wc aggregator. The custom-element registry lives in `cc-widgets/src/wc.ts` — cross-check element names/attrs there. |
| `cc-digital-channels` (`@webex/cc-digital-channels`, `packages/contact-center/cc-digital-channels`) | DRAFT | Tier-1 digital-channels widget (`widget-cc-digital-channels`). Verify against `cc-digital-channels/src/`. |
| `station-login` (`@webex/cc-station-login`, `packages/contact-center/station-login`) | DRAFT | Tier-1 widget. Verify against `station-login/src/`. |
| `user-state` (`@webex/cc-user-state`, `packages/contact-center/user-state`) | DRAFT | Tier-1 widget. Verify state/idle-code logic against `user-state/src/helper.ts`. |
| `task` (`@webex/cc-task`, `packages/contact-center/task`) | DRAFT | Tier-1 bundle of sub-widgets CallControl, CallControlCAD, IncomingTask, OutdialCall, TaskList. Verify per-widget behavior against `task/src/{Widget}/index.tsx` and `task/src/helper.ts`. |
| `ui-logging` (`@webex/cc-ui-logging`, `packages/contact-center/ui-logging`) | DRAFT | Tier-2 telemetry. `withMetrics` HOC + `metricsLogger` in `ui-logging/src/`. |
| `test-fixtures` (`@webex/test-fixtures`, `packages/contact-center/test-fixtures`) | DRAFT | Tier-2 shared mocks/helpers in `test-fixtures/src/`. |
| `meetings-widgets` (`@webex/widgets`, `packages/@webex/widgets`) | DRAFT | Tier-2 legacy meetings family, separate from the CC widget family. Out of scope for CC rules below unless explicitly named. |

## Autonomy & Ask-First

- **May proceed:** read-only research; bug fixes and feature work scoped to a single CC widget package that follow the established Widget → Hook → Component → Store flow; adding unit/E2E tests; doc edits under `ai-docs/`; copy/string and styling tweaks.
- **Ask first / plan + confirm:** changes to the dependency direction or to `cc-widgets/src/wc.ts` custom-element names/attributes (a public contract); changes to the store's observable shape or SDK access surface in `store/src/`; new third-party dependencies; touching the `@webex/widgets` legacy meetings family.
- **Never without explicit human approval:** `git push`, opening/merging PRs, or any deploy. PRs target the `next` branch and are draft by default.

## Naming

Grounded in `patterns/typescript-patterns.md` and the real code:

- Interfaces are prefixed with `I` and PascalCase: `IUserState`, `IStationLoginProps`, `IContactCenter` (`store/src/store.types.ts`, `task/src/task.types.ts`). Never an un-prefixed `UserState` interface.
- Components are PascalCase in `.tsx` files: `UserState.tsx`, `CallControl/index.tsx`. Hooks are camelCase with a `use` prefix in `.ts` files: `useUserState`, `useCallControl` (`*/src/helper.ts`).
- Types are co-located in `{name}.types.ts` (e.g. `user-state/src/user-state.types.ts`); derive subsets with `Pick`/`Partial` rather than re-declaring (e.g. `IUserStateProps = Pick<IUserState, 'onStateChange'>`).
- Event/state names are enums in SCREAMING_SNAKE_CASE values, e.g. `CC_EVENTS.AGENT_STATE_CHANGED`, `TASK_EVENTS.TASK_INCOMING`. Constants are SCREAMING_SNAKE_CASE.

## Logging

- Use the `ui-logging` helpers — `withMetrics` HOC and `metricsLogger` (`ui-logging/src/index.ts`, `ui-logging/src/metricsLogger.ts`) — and the store `logger` passed into hooks. Calls carry a structured context object `{module, method}` (see `task/src/helper.ts` `loadBuddyAgents`: `logger.info('Loaded N buddy agents', {module: 'helper.ts', method: 'loadBuddyAgents'})`).
- **NEVER log PII or credentials** (agent identifiers in sensitive contexts, dial numbers, tokens, session secrets). Severity: high. Verification: review + grep for new `console.*`/`logger.*` calls in a diff. See Security below.
- Prefer the injected `logger` over bare `console.*`; `console.error` is tolerated only inside hook catch blocks where no logger is available.

## Error Handling

- Every widget MUST be wrapped in an `ErrorBoundary` (`react-error-boundary`) at its exported boundary, with `onError` routing to `store.onErrorCallback?.('WidgetName', error)` and a non-throwing fallback (`patterns/react-patterns.md`; pattern realized across `*/src/{Widget}/index.tsx`). Severity: high. Verification: review only.
- SDK/async calls in hooks (`helper.ts`) MUST be wrapped in `try/catch`; on failure, log via the injected `logger` and invoke the optional `onError`-style callback rather than letting the rejection escape. Never swallow an error silently.
- Surface user-friendly errors in the presentational component (loading/error/empty states); never leak raw SDK errors or stack traces to the UI.

## Styling

- In `*.style.scss` / `*.scss` files, use `rem` (not `px`) for spacing, sizing, border-radius, border-width,
and box-shadow offsets, and Momentum theme tokens (`var(--mds-color-theme-*)`) for color instead of hex
codes; don't hardcode `font-size`/`font-weight` when the Momentum `Text` component's `type` prop already
controls it. Exceptions: media-query breakpoints and pixel-perfect image/SVG asset dimensions may stay
`px`. See [`rules/css-rem-units.md`](rules/css-rem-units.md). Severity: low. Verification: review only
(no Stylelint config in this repo yet).

## Imports / Dependencies

**Dependency flow is one direction only** (`.sdd/manifest.json`; enforced by review, source: legacy task-router rule "Circular Dependency Prevention"):

```
cc-widgets → widget packages (station-login, user-state, task) → cc-components → store → @webex/contact-center SDK
```

- A widget package MUST NOT import from `@webex/cc-widgets`. `cc-components` MUST NOT import from any widget package. No package imports upstream. Severity: high — if a circular import is detected, STOP and refactor.
- Access the SDK ONLY through the store: `store.cc.methodName()` / `store.getBuddyAgents()` (`task/src/helper.ts:519`). NEVER `import ... from '@webex/contact-center'` in widget or component code. Severity: high.
- Import the store as the singleton default export: `import store from '@webex/cc-store'`. Never `new Store()`; the instance comes from `Store.getInstance()` (`store/src/store.ts:64`).
- New third-party dependencies require maintainer approval (ask-first).

## Testing

Grounded in `patterns/testing-patterns.md`:

- Unit/component tests use Jest + React Testing Library and live in each package's `tests/` folder; E2E tests use Playwright under `playwright/` at repo root.
- Each behavior gets both a positive test and the relevant negative/guard test (e.g. error path fires `onError`, callback NOT called on failure). Test behavior via `data-testid`, not CSS selectors or implementation details.
- Mock the store with `@webex/test-fixtures` (`test-fixtures/src/`); never hit the real SDK.
Expand All @@ -65,12 +84,14 @@ Grounded in `patterns/testing-patterns.md`:
- Run tests with `yarn workspace @webex/{pkg} test:unit` (single package) or `yarn test:cc-widgets` (all CC); styles via `yarn test:styles`; E2E via `yarn test:e2e`. **Never** `npx jest` directly.

## Security

- No PII or credentials in logs (see Logging) — agent dial numbers, tokens, session/auth material must never reach `logger`/`console` or telemetry payloads.
- Reach the SDK only through the store; never import the SDK directly (prevents bypassing the store's auth/state boundary).
- No hardcoded secrets/tokens/keys anywhere (see Secrets Policy). Sanitize user-provided input rendered in the UI.
- This repo owns NO persistent datastore — all domain data comes from the SDK at runtime, so there is no at-rest data-handling surface here (N/A by construction). A dedicated `SECURITY.md` is not yet present; these rules are the current security posture.

## Spec-Currency & Drift Thresholds

- Update the spec/docs in the SAME change as the code (spec-currency: `.sdd/coverage-policy.defaults.yaml` `specCurrency.sameChangeRequired: true`).
- Drift thresholds mirror `.sdd/coverage-policy.defaults.yaml` (drift = share of spec claims no longer matching code):
- AUTHORITATIVE ≤ 5%
Expand All @@ -79,15 +100,19 @@ Grounded in `patterns/testing-patterns.md`:
- NONE — no spec to drift from

## Secrets Policy

- No hardcoded secrets/tokens/keys/connection strings — ever. The widgets receive auth context from the host application via the SDK/store at runtime; nothing is sourced from a committed file. Never log secrets, never commit them.

## Concurrency & Async

The repo is reactive (MobX) and event-driven (SDK events), so these apply:

- All store mutations MUST go through `runInAction(() => { ... })` — never mutate observable state directly (`patterns/mobx-patterns.md`; realized in `store/src/storeEventsWrapper.ts`). Severity: high. Verification: review only.
- Widgets that read store state MUST be wrapped in `observer()` from `mobx-react-lite` so re-renders track observable reads. Severity: high.
- SDK event subscriptions registered in `useEffect` MUST be torn down in the cleanup return (`cc.on(...)` paired with `cc.off(...)`) to avoid duplicate handlers/leaks (`patterns/react-patterns.md`).
- `cc` is held as `observable.ref` (`store/src/store.ts`) — replace the reference, don't deep-mutate the SDK instance.

## Maintenance

- Add a rule when a review correction recurs; remove it when a lint rule starts enforcing it.
- Cross-reference: patterns → `patterns/` (`typescript-patterns.md`, `react-patterns.md`, `mobx-patterns.md`, `testing-patterns.md`); module specs → `SPEC_INDEX.md`.
78 changes: 78 additions & 0 deletions ai-docs/rules/css-rem-units.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!-- ───────────────────────────────
Template: Rule (example)
Template-ID: rule
Generates: ai-docs/rules/<name>.md
Description: One enforceable repo rule — the rule, its rationale, how to follow it, and how it's enforced.
Library ver: 0.1.0-draft
Last updated: 2026-07-20
─────────────────────────────── -->

# Rule: Use rem units in component styles, not px

> Start here → repo root [`AGENTS.md`](../../AGENTS.md) (agent entry, carries the critical rules) · router [`SPEC_INDEX.md`](../SPEC_INDEX.md). This is an `ai-docs/rules/` fill-in; the folder README explains generic-vs-per-language routing; the repo-wide rules digest is `../RULES.md`.
> Context-efficiency: link to canonical docs — don't duplicate them; one rule per file; defer to the linter where it enforces.

## Rule

In `*.style.scss` / `*.scss` files under `packages/contact-center/*/src/`, express spacing, sizing,
border-radius, border-width, and box-shadow offsets in `rem`, not hardcoded `px`. Use Momentum design
tokens (`var(--mds-color-theme-*)`) for color instead of hex codes, and avoid hardcoding `font-size` /
`font-weight` when the Momentum `Text` component's `type` prop already controls that typography.

## Why

This repo's component styles are built on the Momentum design system, which defines its own type scale
and spacing scale in `rem` (root-relative units respect the user/host app's base font size; `px` does
not). Hardcoded `px` values silently drift from the design tokens and break if a host app changes its
root font size for accessibility. This was flagged in review on PR #719 (`e911-modal.style.scss` used
`px` spacing and hex colors instead of the established convention).

## How to follow

- Convert `px` to `rem` at a 16px base (e.g. `8px` → `0.5rem`, `4px` → `0.25rem`, `1px` → `0.0625rem`).
See the inline `// Npx to rem` comments in
`packages/contact-center/cc-components/src/components/task/CallControl/call-control.styles.scss`.
- Reference:
```scss
// from packages/contact-center/cc-components/src/components/task/CampaignErrorDialog/campaign-error-dialog.style.scss
.campaign-error-dialog {
width: 25rem;
border-radius: 0.5rem;
padding: 1rem;
box-shadow:
0rem 0.25rem 0.5rem 0rem rgba(0, 0, 0, 0.16),
0rem 0rem 0.0625rem 0rem rgba(0, 0, 0, 0.16);
}
```
Incorrect (pre-fix `e911-modal.style.scss`):
```scss
.e911-modal {
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}
.e911-warning-box {
background-color: #fff3cd; // hardcoded hex instead of a Momentum token
border: 1px solid #ffc107;
}
.e911-modal-title {
font-size: 18px; // redundant: Text type="body-large-bold" already sets this
font-weight: 600;
}
```
- Use Momentum theme tokens for color, not hex: `var(--mds-color-theme-background-alert-warning-normal)`,
`var(--mds-color-theme-outline-warning-normal)`, `var(--mds-color-theme-text-warning-normal)`,
`var(--mds-color-theme-text-secondary-normal)`, `var(--mds-color-theme-text-accent-normal)`,
`var(--mds-color-theme-outline-primary-normal)` (see `call-control.styles.scss`, `user-state.scss`).
- Don't set `font-size`/`font-weight` in SCSS for text rendered through the Momentum `Text` component —
its `type` prop (e.g. `type="body-large-bold"`) already governs typography.
- **Exceptions:** media-query breakpoints (`@media (max-width: 600px)`) and exact pixel-perfect image/SVG
asset dimensions (e.g. a background-image sized to a specific icon export) may stay in `px` — see the
same `call-control.styles.scss` file for both cases. Don't over-apply the rule to these.

## Enforced by

Review only. `yarn test:styles` runs ESLint (`"test:styles": "eslint"` in each package's
`package.json`), not a CSS/SCSS linter — there is no Stylelint config in this repo, so nothing currently
bans `px` or hex colors in `.scss` files automatically. Consider adding Stylelint with
`declaration-property-unit-disallowed-list` (scoped to spacing/sizing properties) and `color-no-hex` to
make this automatic.
Loading
Loading