diff --git a/.gitignore b/.gitignore index 9aba0c8a7..f74fd6d85 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ reports/* .python-version .cache coverage/ +.generated/ +.agents/ +.claude/ +.cursor/skills/ +.cursor/sdlc-plugins/ diff --git a/.sdd/config/sdd-manifest.schema.json b/.sdd/config/sdd-manifest.schema.json new file mode 100644 index 000000000..1d0a626ce --- /dev/null +++ b/.sdd/config/sdd-manifest.schema.json @@ -0,0 +1,1133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://wbx.example/sdd/sdd-manifest.schema.json", + "title": "SDD Manifest", + "description": "Machine-readable contract for a spec-driven repository: coverage status, delta specs, WHAT+WHY, provenance, and validation runtime separation. The validator's methodology axis (spec-validator Axis-A) checks specs against this file. Lives at .sdd/manifest.json in the target repo. Repo-pinned 0.2.2 extension at .sdd/config/sdd-manifest.schema.json (test_index_path, template_library_*, has_submodules, validation status fields) until upstream plugin schema catches up.", + "type": "object", + "additionalProperties": false, + "required": [ + "manifest_version", + "repository", + "topology", + "commands", + "modules", + "spec_policy", + "validation", + "substrate" + ], + "properties": { + "manifest_version": { + "type": "integer", + "minimum": 1, + "description": "Schema version this manifest targets." + }, + "repository": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "purpose", + "category" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "purpose": { + "type": "string", + "minLength": 1, + "description": "One sentence: what this repo is." + }, + "category": { + "type": "string", + "enum": [ + "greenfield", + "cat1-legacy", + "cat2-legacy" + ], + "description": "History-depth axis (NOT part of the topology/mode classifier). greenfield | cat1-legacy (rich commit/PR history) | cat2-legacy (>10yr, history/tribal-knowledge lost). Drives only whether doc-backfill may use commit history for WHY. See config/classification.md." + }, + "primary_languages": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "Drives patterns/ and rules/ CONTENT (not section shape)." + } + } + }, + "topology": { + "type": "string", + "enum": [ + "Multi-repo-workspace", + "Orchestrator-plus-components", + "Single-repo" + ], + "description": "The doc tree mirrors this. Set by repo-bootstrap." + }, + "commands": { + "type": "object", + "description": "Real, runnable commands read from the repo's actual build config (never guessed). Keys are task names. String values are retained for backward compatibility; object values carry provenance for validator checks.", + "minProperties": 1, + "additionalProperties": { + "oneOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "command", + "source_file" + ], + "properties": { + "command": { + "type": "string", + "minLength": 1 + }, + "source_file": { + "type": "string", + "minLength": 1, + "description": "Build/config file where this command was discovered." + }, + "verified_at": { + "type": "string", + "format": "date-time" + } + } + } + ] + } + }, + "coverage_status_definitions": { + "type": "object", + "description": "SDD coverage status = how complete the SPEC is for a module (distinct from AI-autonomy zones \u2014 do not conflate). Defaults shown; a repo may tighten thresholds, never loosen below these.", + "additionalProperties": false, + "properties": { + "specced": { + "type": "string", + "default": ">=80% public surface specced, drift <5% \u2014 spec is authoritative" + }, + "partial": { + "type": "string", + "default": "40-80% specced \u2014 spec is a hint, cross-check code" + }, + "untracked": { + "type": "string", + "default": "<40% specced \u2014 code is the source of truth" + } + } + }, + "modules": { + "type": "array", + "minItems": 1, + "description": "Every module that gets (or will get) dedicated SDD docs.", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "coverage_status", + "coverage_evidence" + ], + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "coverage_status": { + "type": "string", + "enum": [ + "Specced", + "Partial", + "Untracked" + ] + }, + "coverage_evidence": { + "type": "string", + "minLength": 1, + "description": "WHY this status \u2014 e.g. 'public API 82% documented, last 5 PRs zero MODIFIED requirements'. Not a bare percentage and not a path to a local generated report." + }, + "canonical_spec": { + "type": "string", + "description": "Path to the module's canonical spec, combining orientation and detailed behavior, or omitted if none yet (Untracked)." + }, + "architecture_doc": { + "type": "string", + "description": "Optional path to a separate module architecture document for legacy or unusually large modules. New SDD bootstrap runs should normally use canonical_spec only." + }, + "contracts": { + "type": "object", + "description": "The module's current Provides/Requires baseline. Later code and spec changes are measured against this.", + "additionalProperties": false, + "properties": { + "provides": { + "type": "array", + "items": { + "type": "string" + } + }, + "requires": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "characterization_baseline": { + "type": "string", + "description": "Path to the characterization-test suite pinning current behavior. Required before modifying a Partial/Untracked module." + }, + "last_assessed": { + "type": "string", + "format": "date" + }, + "section_profile": { + "type": "object", + "description": "Per-module section-selection profile resolving module-spec Include-if conditions (M-1 through M-16). true = KEEP, false = DROP, null = PENDING (needs human input). Written by brownfield-questionnaire; consumed by doc-emitting skills and spec-validator A11. Per-field audit trail lives in the decision log.", + "additionalProperties": false, + "properties": { + "has_ui": { + "type": [ + "boolean", + "null" + ], + "description": "M-1: Does this module have a UI? Resolves module-spec \u00a7 UI flow per use case." + }, + "crosses_service_boundaries": { + "type": [ + "boolean", + "null" + ], + "description": "M-2: Does this module call other services over the network? Resolves module-spec \u00a7 cross-service flow." + }, + "enforces_domain_rules": { + "type": [ + "boolean", + "null" + ], + "description": "M-3: Does it enforce domain rules or entity invariants? Resolves module-spec \u00a7 Business Rules & Invariants." + }, + "is_concurrent_async": { + "type": [ + "boolean", + "null" + ], + "description": "M-4: Is it concurrent, async, reactive, or event-driven? Resolves module-spec \u00a7 Concurrency & Reactive Flow." + }, + "owns_persistence": { + "type": [ + "boolean", + "null" + ], + "description": "M-5: Does it own persistence (its own tables/store)? Resolves module-spec \u00a7 Data / Schema + \u00a7 Schema / Migration Discipline." + }, + "returns_caller_errors": { + "type": [ + "boolean", + "null" + ], + "description": "M-6: Does it return/raise errors a caller must handle? Resolves module-spec \u00a7 Error Handling & Failure Modes." + }, + "has_design_tradeoff": { + "type": [ + "boolean", + "null" + ], + "description": "M-7: Is there a non-obvious design trade-off a consumer must know? Resolves module-spec \u00a7 Key Design Trade-off. Typically requires human input." + }, + "stateful_transitions": { + "type": [ + "boolean", + "null" + ], + "description": "M-8: Does the module have non-trivial state transitions? Resolves module-spec \u00a7 State Machine." + }, + "exposes_wire_protocol": { + "type": [ + "boolean", + "null" + ], + "description": "M-9: Does it expose a wire protocol or binary format? Resolves module-spec \u00a7 Protocol / Wire Format." + }, + "ui_multi_screen": { + "type": [ + "boolean", + "null" + ], + "description": "M-10: Is it a UI with a multi-screen flow? Resolves module-spec \u00a7 UI Flow." + }, + "large_data_model": { + "type": [ + "boolean", + "null" + ], + "description": "M-11: Does it own a large or complex data model? Resolves module-spec \u00a7 Data Model." + }, + "has_tiers": { + "type": [ + "boolean", + "null" + ], + "description": "M-12: Does the repo tier its modules? Resolves module-spec \u00a7 Tier." + }, + "module_specific_conventions": { + "type": [ + "boolean", + "null" + ], + "description": "M-13: Does it have conventions beyond repo-wide rules? Resolves module-spec \u00a7 Module Do's / Don'ts." + }, + "published_package": { + "type": [ + "boolean", + "null" + ], + "description": "M-14: Is it published/consumed as a package? Resolves module-spec \u00a7 Export Stability." + }, + "embedded_in_host": { + "type": [ + "boolean", + "null" + ], + "description": "M-15: Is it embedded into a host application? Resolves module-spec \u00a7 Host Integration & Theming." + }, + "holds_client_state": { + "type": [ + "boolean", + "null" + ], + "description": "M-16 [INFERRED \u2014 template has this condition but plan omits it from M-1..M-15]: Does this module hold client-side state? Resolves module-spec \u00a7 State Model." + }, + "resolved_by": { + "type": "string", + "description": "Who resolved this module's profile (agent + runtime, or human)." + }, + "resolved_at": { + "type": "string", + "format": "date-time", + "description": "When this module's profile was last resolved." + }, + "has_submodules": { + "type": [ + "boolean", + "null" + ], + "description": "Does this module contain child modules that own their own specs? Resolves module-spec \u00a7 Sub-modules." + } + } + }, + "source_policy": { + "type": "object", + "description": "Module-specific routing for older specs/docs discovered before SDD onboarding. Agents use canonical_spec; this object explains how older material was handled.", + "additionalProperties": false, + "required": [ + "mode", + "conflict_status" + ], + "properties": { + "mode": { + "type": "string", + "enum": [ + "keep-separate", + "migrate-existing", + "reconcile" + ] + }, + "existing_sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "conflict_status": { + "type": "string", + "enum": [ + "none", + "pending", + "resolved" + ], + "description": "pending means reconciliation found a conflict that must be resolved before this module's canonical spec can be promoted." + }, + "decision_record": { + "type": "string", + "description": "Decision log or run record that explains this module's policy choice and any conflict resolution." + } + } + } + } + } + }, + "spec_source_policy": { + "type": "object", + "description": "Explicit policy chosen when onboarding detected existing specs/docs. If existing specs are present and this object is absent, spec-source-policy must stop and ask for a decision.", + "additionalProperties": false, + "required": [ + "mode", + "decided_by", + "decided_at", + "decision_record" + ], + "properties": { + "mode": { + "type": "string", + "enum": [ + "keep-separate", + "migrate-existing", + "reconcile" + ], + "description": "No default is allowed. The user must choose the policy before spec-writing continues." + }, + "decided_by": { + "type": "string", + "minLength": 1 + }, + "decided_at": { + "type": "string", + "format": "date-time" + }, + "decision_record": { + "type": "string", + "minLength": 1 + } + } + }, + "spec_sources": { + "type": "array", + "description": "Existing specs/docs or native contract/API sources discovered before SDD onboarding. Entries route old material as context/source only unless explicitly made canonical through the normal canonical_spec fields. Native schema/API sources may remain authoritative for exact contracts while SDD markdown links to them.", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "scope", + "role", + "canonical", + "use_by_agents" + ], + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "scope": { + "type": "string", + "enum": [ + "repo", + "module", + "feature", + "reference" + ], + "description": "Classification of an existing source document. feature is retained only so migration can route legacy change-specific source material." + }, + "module": { + "type": "string", + "description": "Module path when scope is module." + }, + "role": { + "type": "string", + "enum": [ + "reference-only", + "source-material", + "superseded", + "conflict-pending" + ] + }, + "canonical": { + "type": "boolean", + "description": "False for preserved older specs. Agents use modules[].canonical_spec and manifest-routed standing docs." + }, + "use_by_agents": { + "type": "string", + "enum": [ + "context-only", + "source-for-migration", + "source-for-reconciliation", + "ignore" + ] + }, + "canonical_target": { + "type": "string", + "description": "Canonical SDD path selected by onboarding or migration routing for this source, when applicable." + }, + "notes": { + "type": "string", + "description": "Use this to record how an older spec or native contract/API source is linked from the generated SDD docs." + } + } + } + }, + "spec_policy": { + "type": "object", + "additionalProperties": false, + "required": [ + "delta_grammar", + "require_what_and_why", + "require_provenance", + "required_sections_by_change_class" + ], + "properties": { + "delta_grammar": { + "type": "object", + "description": "Every spec change is a delta against canonical spec sections.", + "additionalProperties": false, + "required": [ + "added", + "modified", + "removed" + ], + "properties": { + "added": { + "type": "string", + "default": "## ADDED Requirements" + }, + "modified": { + "type": "string", + "default": "## MODIFIED Requirements" + }, + "removed": { + "type": "string", + "default": "## REMOVED Requirements" + } + } + }, + "require_what_and_why": { + "type": "boolean", + "default": true, + "description": "Every requirement (incl. ADDED, not only MODIFIED) must state WHAT and WHY. Validator Axis-A fails the spec otherwise." + }, + "require_provenance": { + "type": "boolean", + "default": true, + "description": "Every spec/delta records author, approver, date." + }, + "protected_specs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Canonical specs that must never be overwritten in place. A change to one must go through spec-reconcile and land as a MODIFIED delta with migration. This is the no-override guard." + }, + "required_sections_by_change_class": { + "type": "object", + "description": "Conditional section library: which spec sections are required for which class of change. Core is always required; the rest are gated so a trivial change isn't taxed with a 12-section spec.", + "additionalProperties": false, + "properties": { + "core_always": { + "type": "array", + "default": [ + "Intent (WHAT)", + "Rationale (WHY)", + "Scope/Out-of-scope", + "Acceptance criteria", + "Contracts delta" + ], + "items": { + "type": "string" + } + }, + "security_or_contract_or_perf_critical": { + "type": "array", + "default": [ + "Data", + "Error Matrix", + "Resilience", + "Observability", + "Operations" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "validation": { + "type": "object", + "additionalProperties": false, + "required": [ + "generator_runtime", + "blocking_severities" + ], + "anyOf": [ + { + "required": [ + "generator_runtime_source", + "minimum_independence" + ] + }, + { + "required": [ + "must_differ" + ] + } + ], + "properties": { + "generator_runtime": { + "type": "string", + "minLength": 1, + "description": "Opaque runtime identifier resolved from the active generation context when execution begins. Runtime names are not enumerated or inferred from model families, credential variables, or skill installation paths." + }, + "generator_model": { + "type": "string", + "minLength": 1, + "description": "Opaque model identifier supplied by the active generation context, when available." + }, + "generator_runtime_source": { + "type": "string", + "enum": [ + "host-metadata", + "invocation-input", + "environment", + "human-provided" + ], + "description": "How generator_runtime was obtained. Environment resolution uses explicit SDD_RUNTIME_ID input only; provider credentials and model-family heuristics are not runtime identity." + }, + "validator_runtime": { + "type": "string", + "minLength": 1, + "description": "Opaque runtime identifier independently resolved from the active validation context. Written during validator preflight and absent until validation runs." + }, + "validator_model": { + "type": "string", + "minLength": 1, + "description": "Opaque model identifier supplied by the active validation context, when available." + }, + "validator_runtime_source": { + "type": "string", + "enum": [ + "host-metadata", + "invocation-input", + "environment", + "human-provided" + ], + "description": "How validator_runtime was independently obtained." + }, + "minimum_independence": { + "type": "string", + "enum": [ + "different-runtime", + "different-model", + "different-session" + ], + "default": "different-runtime", + "description": "Repository-approved minimum separation. different-runtime is the default. Lower levels must be explicitly selected by a human before validation; the validator never silently weakens this policy." + }, + "must_differ": { + "type": "boolean", + "const": true, + "description": "Deprecated compatibility field from earlier manifests. When present it means generation and validation execution identities must differ; minimum_independence defines the enforceable level." + }, + "runtime_fallback_tier": { + "type": "string", + "enum": [ + "different-runtime", + "different-model", + "different-session" + ], + "description": "Separation established by validator preflight. Different-model requires both model ids and different-session requires both run ids; self-asserted freshness is not evidence. The value must satisfy minimum_independence." + }, + "blocking_severities": { + "type": "array", + "default": [ + "Blocking" + ], + "items": { + "type": "string", + "enum": [ + "Blocking", + "Important", + "Medium", + "Minor" + ] + }, + "description": "Severities that fail the gate." + }, + "generator_run_id": { + "type": "string", + "minLength": 1, + "description": "Opaque generation run or session id supplied by the active execution context, when available; do not use a local report path." + }, + "validator_run_id": { + "type": "string", + "minLength": 1, + "description": "Opaque validation run or session id independently supplied by the active execution context, when available; required to establish different-session separation." + }, + "source_commit": { + "type": "string", + "description": "Target repo commit SHA used for generation/validation." + }, + "base_ref": { + "type": "string" + }, + "head_ref": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "not-run", + "pass", + "pass-with-warnings", + "blocked" + ], + "description": "Latest independent validator verdict. not-run until Session B records a HEAD result." + }, + "validated_at": { + "type": "string", + "description": "Date or timestamp of the recorded validator run." + }, + "blocking_count": { + "type": "integer", + "minimum": 0 + }, + "warning_count": { + "type": "integer", + "minimum": 0 + }, + "evidence_summary": { + "type": "string", + "description": "Short measured evidence summary. Must not cite local .generated/ report paths." + } + }, + "allOf": [ + { + "if": { + "required": [ + "validator_runtime" + ] + }, + "then": { + "required": [ + "validator_runtime_source", + "runtime_fallback_tier" + ] + } + }, + { + "if": { + "required": [ + "runtime_fallback_tier" + ], + "properties": { + "runtime_fallback_tier": { + "const": "different-model" + } + } + }, + "then": { + "required": [ + "generator_model", + "validator_model" + ] + } + }, + { + "if": { + "required": [ + "runtime_fallback_tier" + ], + "properties": { + "runtime_fallback_tier": { + "const": "different-session" + } + } + }, + "then": { + "required": [ + "generator_run_id", + "validator_run_id" + ] + } + }, + { + "if": { + "required": [ + "minimum_independence", + "runtime_fallback_tier" + ], + "properties": { + "minimum_independence": { + "const": "different-runtime" + } + } + }, + "then": { + "properties": { + "runtime_fallback_tier": { + "const": "different-runtime" + } + } + } + }, + { + "if": { + "required": [ + "minimum_independence", + "runtime_fallback_tier" + ], + "properties": { + "minimum_independence": { + "const": "different-model" + } + } + }, + "then": { + "properties": { + "runtime_fallback_tier": { + "enum": [ + "different-runtime", + "different-model" + ] + } + } + } + } + ] + }, + "layout": { + "type": "object", + "description": "Repository-specific SDD layout. Default generated docs live under ai-docs; generators must honor these paths and rewrite template navigation links accordingly instead of assuming docs/templates or docs/modules.", + "additionalProperties": false, + "properties": { + "sdd_root": { + "type": "string", + "default": ".sdd" + }, + "docs_root": { + "type": "string", + "default": "ai-docs" + }, + "agent_entry_path": { + "type": "string", + "default": "AGENTS.md", + "description": "Root agent entry file kept at repo root for tool auto-discovery." + }, + "standing_docs_root": { + "type": "string", + "default": "ai-docs", + "description": "Default root folder for standing SDD docs such as ARCHITECTURE, RULES, CONTRACTS, and SPEC_INDEX." + }, + "spec_index_path": { + "type": "string", + "default": "ai-docs/SPEC_INDEX.md" + }, + "module_docs_strategy": { + "type": "string", + "enum": [ + "source-local", + "central", + "existing-convention" + ], + "default": "source-local" + }, + "module_docs_root": { + "type": "string", + "description": "Only used when module_docs_strategy is central or a repo explicitly chooses a shared docs folder." + }, + "module_docs_folder_name": { + "type": "string", + "default": "ai-docs" + }, + "template_roots": { + "type": "object", + "additionalProperties": false, + "properties": { + "canonical": { + "type": "string", + "default": ".sdd/templates" + }, + "extensions": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + ".sdd/templates/extensions" + ], + "description": "Template extension roots loaded after canonical templates, recursively and in lexical order. Mirrored relative paths override canonical files; extension-only templates use .ext.template.md and a Template-ID ending in -ext." + }, + "legacy_compat": { + "type": "string", + "description": "Optional read-only migration path for older repos. The canonical template root remains template_roots.canonical, not ai-docs." + } + } + }, + "repo_skills_root": { + "type": "string", + "default": ".sdd/skills" + }, + "runtime_skill_entrypoints": { + "type": "object", + "description": "Optional map from an opaque runtime id to the skill entrypoint used by that installation. Runtime ids and paths are discovered or supplied at installation/execution time; the plugin does not enumerate them.", + "additionalProperties": { + "type": "string", + "minLength": 1 + } + }, + "contracts_strategy": { + "type": "string", + "enum": [ + "root-index-module-detail", + "root-catalog", + "module-only" + ], + "default": "root-index-module-detail" + }, + "test_index_path": { + "type": "string", + "description": "Path to the repo-wide TEST_INDEX.md standing doc." + } + } + }, + "tooling": { + "type": "object", + "description": "Tool/source provenance for generated SDD assets. Records which SDD-Bootstrap checkout produced the repo state.", + "additionalProperties": false, + "properties": { + "sdlc_skills": { + "type": "object", + "additionalProperties": false, + "properties": { + "source_repo": { + "type": "string", + "description": "Git remote URL or local source identifier for the SDD-Bootstrap checkout used to generate assets." + }, + "source_ref": { + "type": "string" + }, + "source_branch": { + "type": "string" + }, + "install_mode": { + "type": "string", + "enum": [ + "copy", + "symlink", + "unknown" + ] + }, + "installed_at": { + "type": "string", + "format": "date-time" + }, + "plugins": { + "type": "array", + "items": { + "type": "string" + } + }, + "template_library_version": { + "type": "string", + "description": "Pinned SDLC template-library semver used to generate standing docs and module specs." + }, + "template_library_ref": { + "type": "string", + "description": "Pinned SDLC template-library git SHA." + } + } + } + } + }, + "coverage_ratchet": { + "type": "object", + "description": "Once a module reaches a coverage status, it cannot be downgraded except via an expiring, approver-signed waiver. Direction of travel is up.", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "default": true + }, + "waivers": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "reason", + "expires", + "approved_by" + ], + "properties": { + "module": { + "type": "string" + }, + "reason": { + "type": "string", + "minLength": 1 + }, + "expires": { + "type": "string", + "format": "date" + }, + "approved_by": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "substrate": { + "type": "object", + "description": "The repo-standards layer this repo CONSUMES (does not author). Bootstrap detects and leaves these alone.", + "additionalProperties": false, + "required": [ + "source", + "consumed" + ], + "properties": { + "source": { + "type": "string", + "default": "WebexTools/repo-standards" + }, + "consumed": { + "type": "array", + "items": { + "type": "string" + }, + "description": "e.g. ['lint/format configs', 'standard files (README/SECURITY/CODEOWNERS)', 'base AGENTS.md']" + }, + "compliance_tier": { + "type": "string", + "enum": [ + "legacy", + "baseline", + "standard", + "strict" + ], + "description": "The repo-standards tier this repo runs at, if any. Recorded for cross-reference only; coverage status is the SDD axis and is tracked separately." + } + } + }, + "section_profiles": { + "type": "object", + "description": "Questionnaire-resolved section-selection profiles for repo-level Include-if conditions. Per-module profiles live inside each module item. true = KEEP section, false = DROP section, null = PENDING (emit as scaffold with [NEEDS HUMAN INPUT]). Written by brownfield-questionnaire; consumed by doc-emitting skills and spec-validator A11.", + "additionalProperties": false, + "properties": { + "repo": { + "type": "object", + "description": "Repo-level profile resolving ARCHITECTURE.template.md Include-if conditions (R-1 through R-14).", + "additionalProperties": false, + "properties": { + "owns_datastore": { + "type": [ + "boolean", + "null" + ], + "description": "R-1: Does this repo own a datastore (persists its own data)? Resolves ARCHITECTURE \u00a7 Data & Schema." + }, + "holds_client_state": { + "type": [ + "boolean", + "null" + ], + "description": "R-2: Does this repo hold client-side state (UI store, in-memory model)? Resolves ARCHITECTURE \u00a7 State Model." + }, + "components_interact": { + "type": [ + "boolean", + "null" + ], + "description": "R-3: Do components/services call each other or exchange events? Resolves ARCHITECTURE \u00a7 Dependency / Interaction Topology." + }, + "domain_data_across_components": { + "type": [ + "boolean", + "null" + ], + "description": "R-4: Does the repo own domain data spread across components? Resolves ARCHITECTURE \u00a7 Object / Data Ownership." + }, + "caches_data": { + "type": [ + "boolean", + "null" + ], + "description": "R-5: Does this repo cache data? Resolves ARCHITECTURE \u00a7 Caching Catalog." + }, + "observability_convention": { + "type": [ + "boolean", + "null" + ], + "description": "R-6: Is there a logging/metrics/audit convention worth standardizing? Resolves ARCHITECTURE \u00a7 Observability Patterns." + }, + "deploys_to_infra": { + "type": [ + "boolean", + "null" + ], + "description": "R-7: Does this repo deploy to or depend on infrastructure? Resolves ARCHITECTURE \u00a7 Infrastructure Matrix." + }, + "shared_base_libs": { + "type": [ + "boolean", + "null" + ], + "description": "R-8: Does the repo inherit a shared/base library stack? Resolves ARCHITECTURE \u00a7 Shared / Base Libraries." + }, + "is_monorepo": { + "type": [ + "boolean", + "null" + ], + "description": "R-9: Is this repo a monorepo (multiple packages in one tree)? Resolves ARCHITECTURE \u00a7 Package Map & Inter-Package Dependencies." + }, + "multi_platform": { + "type": [ + "boolean", + "null" + ], + "description": "R-10: Does the repo target multiple platforms? Resolves ARCHITECTURE \u00a7 Platform Matrix." + }, + "published_package": { + "type": [ + "boolean", + "null" + ], + "description": "R-11: Is this repo published/consumed as a package? Resolves ARCHITECTURE \u00a7 Release & Versioning." + }, + "embedded_in_host": { + "type": [ + "boolean", + "null" + ], + "description": "R-12: Is this repo embedded into a host application? Resolves ARCHITECTURE \u00a7 Host Integration & Theming." + }, + "cross_repo_deps_material": { + "type": [ + "boolean", + "null" + ], + "description": "R-13: Are cross-repo dependencies material? Resolves ARCHITECTURE \u00a7 Cross-Repo Dependency Graph." + }, + "security_arch_warranted": { + "type": [ + "boolean", + "null" + ], + "description": "R-14: Does the security architecture warrant its own view? Resolves ARCHITECTURE \u00a7 Security Architecture." + } + } + }, + "resolved_by": { + "type": "string", + "description": "Who resolved the repo-level profile (agent + runtime, or human)." + }, + "resolved_at": { + "type": "string", + "format": "date-time", + "description": "When the repo-level profile was last resolved." + } + } + } + } +} diff --git a/.sdd/manifest.json b/.sdd/manifest.json new file mode 100644 index 000000000..fa3ce0285 --- /dev/null +++ b/.sdd/manifest.json @@ -0,0 +1,818 @@ +{ + "manifest_version": 1, + "repository": { + "name": "react-widgets", + "purpose": "A single Git repository containing independently consumable React, Redux, and embeddable Webex widget packages plus their build and test tooling.", + "category": "cat1-legacy", + "primary_languages": [ + "javascript", + "typescript", + "scss" + ] + }, + "topology": "Single-repo", + "commands": { + "install": { + "command": "npm install --legacy-peer-deps", + "source_file": ".circleci/config.yml" + }, + "build": { + "command": "npm run build:all", + "source_file": "package.json" + }, + "build_widgets": { + "command": "npm run build:widgets", + "source_file": "package.json" + }, + "serve": { + "command": "npm start", + "source_file": "package.json" + }, + "lint": { + "command": "npm run static-analysis", + "source_file": "package.json" + }, + "test": { + "command": "npm run jest", + "source_file": "package.json" + }, + "test_smoke": { + "command": "npm run test:automation:smoke", + "source_file": "package.json" + }, + "test_space": { + "command": "npm run test:automation:space", + "source_file": "package.json" + }, + "test_recents": { + "command": "npm run test:automation:recents", + "source_file": "package.json" + }, + "publish": { + "command": "npm run publish:components", + "source_file": "package.json" + } + }, + "coverage_status_definitions": { + "specced": ">=80% public surface specced, drift <5%, independent validation complete, and last 5 PRs have zero unresolved MODIFIED requirements", + "partial": "40-80% documented, or documentation exceeds 80% but an independent validation/promotion gate is still pending", + "untracked": "<40% specced; code is the source of truth" + }, + "modules": [ + { + "path": "packages/node_modules/@webex/widget-space/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 94%: public exports, events, configuration, state, UI, error paths, and journey intent are covered; the last-five-PR drift gate still blocks promotion.", + "canonical_spec": "ai-docs/modules/space-messaging-spec.md", + "contracts": { + "provides": [ + "Space, Message, Files, and Roster widget package entrypoints", + "current rooms/messages/calls/activity host events" + ], + "requires": [ + "widget runtime/auth", + "Redux resource modules", + "Webex SDK and Mercury", + "shared UI and containers" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "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": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@webex/widget-space/README.md", + "packages/node_modules/@webex/widget-space/events.md", + "test/journeys/testplan.md", + "packages/node_modules/@ciscospark/widget-*/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/widget-recents/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 95%: exports, filters, events, state transitions, host forms, and journey intent are covered; the last-five-PR drift gate still blocks promotion.", + "canonical_spec": "ai-docs/modules/recents-spec.md", + "contracts": { + "provides": [ + "Recents widget package entrypoint", + "current room/message/call/membership/action host events" + ], + "requires": [ + "widget runtime/auth", + "spaces/conversation/meeting state", + "Webex SDK and Mercury" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": true, + "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": true, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@webex/widget-recents/README.md", + "packages/node_modules/@webex/widget-recents/events.md", + "test/journeys/testplan.md", + "packages/node_modules/@ciscospark/widget-recents/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/widget-meet/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 92%: meeting creation, join/media/leave state, call UI, errors, and available tests are covered; sparse lifecycle tests and the last-five-PR drift gate block promotion.", + "canonical_spec": "ai-docs/modules/meetings-spec.md", + "contracts": { + "provides": [ + "Meet and Meetings widget package entrypoints", + "meeting Redux actions/reducer" + ], + "requires": [ + "Webex meetings SDK collection", + "media components", + "widget runtime and Space integration" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": true, + "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": true, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@ciscospark/widget-meet/README.md", + "test/journeys/testplan.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/widget-call-history/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 91%: all typed calling widgets, adapters, state/UI/error contracts, and compatibility hazards are inventoried; sparse direct tests and the last-five-PR drift gate block promotion.", + "canonical_spec": "ai-docs/modules/calling-spec.md", + "contracts": { + "provides": [ + "Call History, Speed Dial, Voice Mail, and Number Pad entrypoints" + ], + "requires": [ + "Webex component adapter interfaces/SDK adapter", + "React Hook Form and shared calling UI" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "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": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@webex/widget-call-history/README.md", + "packages/node_modules/@webex/widget-speed-dial/src/README.md", + "packages/node_modules/@webex/widget-speed-dial/src/GenericModal/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/react-component-activity-item/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 93%: 54 public component entrypoints, key coupling, lifecycle, accessibility, host, and test rules are covered; export automation and promotion-history evidence remain.", + "canonical_spec": "ai-docs/modules/shared-ui-components-spec.md", + "contracts": { + "provides": [ + "public react-component package entrypoints", + "shared component utility barrel" + ], + "requires": [ + "React/DOM", + "Momentum UI", + "CSS/SCSS pipeline", + "selected Redux/SDK integrations" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": false, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": false, + "has_design_tradeoff": true, + "stateful_transitions": true, + "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": true, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@ciscospark/react-component-*/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/redux-module-activities/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 94%: all Redux package barrels, async/state conventions, meeting authority, errors, and tests are covered; late-result characterization and promotion-history evidence remain.", + "canonical_spec": "ai-docs/modules/state-management-spec.md", + "contracts": { + "provides": [ + "Redux reducers, actions, thunks, selectors, SDK/auth state" + ], + "requires": [ + "Redux/Immutable.js", + "Webex SDK plugins and Mercury", + "widget store composition" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": false, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": true, + "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": false, + "holds_client_state": true, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@ciscospark/redux-module-*/README.md", + "packages/node_modules/@ciscospark/react-redux-spark*/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/react-container-activity-list/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 92%: all container/HOC entrypoints, projections, state, scrolling, Mercury filters, errors, and lifecycle risks are covered; teardown characterization and promotion-history evidence remain.", + "canonical_spec": "ai-docs/modules/containers-hooks-spec.md", + "contracts": { + "provides": [ + "connected component package entrypoints", + "conversation Mercury and scrollable HOCs" + ], + "requires": [ + "Redux slices/actions", + "shared components", + "Webex SDK/Mercury", + "browser DOM" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": true, + "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": true, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "packages/node_modules/@ciscospark/react-container-*/README.md", + "packages/node_modules/@ciscospark/react-hoc-*/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "packages/node_modules/@webex/webex-widget-base/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 93%: enhancer order, object-shaped constructor, host APIs, auth/current-user state, removal, demos, and README conflict are covered; direct lifecycle tests and promotion-history evidence still block promotion.", + "canonical_spec": "ai-docs/modules/widget-runtime-auth-spec.md", + "contracts": { + "provides": [ + "widget enhancer/runtime entrypoint", + "browser/data APIs and registry", + "sign-in and demo host surfaces" + ], + "requires": [ + "React/Redux", + "Webex SDK/auth", + "browser DOM and CustomEvent", + "widget reducer maps and capability HOCs" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": true, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "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": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "README.md", + "packages/node_modules/@webex/webex-widget-base/README.md", + "packages/node_modules/@webex/widget-demo/README.md", + "packages/node_modules/@ciscospark/spark-widget-base/README.md", + "packages/node_modules/@ciscospark/widget-*-demo/README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + }, + { + "path": "scripts/build/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 95%: every npm/build/publish target, package filter, artifact path, CI promotion gate, SRI rule, and known legacy-script hazard is covered; publish-recovery and promotion-history evidence remain.", + "canonical_spec": "ai-docs/modules/build-release-tooling-spec.md", + "contracts": { + "provides": [ + "repository build/start/test/publish/deploy commands", + "package and CDN artifacts" + ], + "requires": [ + "Node/npm lockfile", + "Babel/Webpack/Rollup", + "CI registry/CDN/Git credentials" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": false, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": false, + "exposes_wire_protocol": false, + "ui_multi_screen": false, + "large_data_model": false, + "has_tiers": false, + "module_specific_conventions": true, + "published_package": false, + "embedded_in_host": false, + "holds_client_state": false, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "README.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved; root README build/release units are accounted for by the repository-scope source-fidelity inventory and routed into this module" + } + }, + { + "path": "test/journeys/", + "coverage_status": "Partial", + "coverage_evidence": "Documentation assessment 96%: Jest and WDIO topology, every protected test-plan suite/scenario family, environment, helpers, async behavior, CI use, and known gaps are covered; calling-journey gaps and promotion-history evidence remain.", + "canonical_spec": "ai-docs/modules/test-automation-spec.md", + "contracts": { + "provides": [ + "Jest unit suites", + "WDIO smoke/Space/Recents/TAP journeys", + "JUnit/browser diagnostics" + ], + "requires": [ + "built widget assets", + "local Selenium or Sauce Labs", + "Webex test users and integration services" + ] + }, + "last_assessed": "2026-07-22", + "section_profile": { + "has_ui": false, + "crosses_service_boundaries": true, + "enforces_domain_rules": true, + "is_concurrent_async": true, + "owns_persistence": false, + "returns_caller_errors": true, + "has_design_tradeoff": true, + "stateful_transitions": false, + "exposes_wire_protocol": false, + "ui_multi_screen": false, + "large_data_model": false, + "has_tiers": false, + "module_specific_conventions": true, + "published_package": false, + "embedded_in_host": false, + "holds_client_state": false, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30", + "has_submodules": false + }, + "source_policy": { + "mode": "reconcile", + "existing_sources": [ + "test/journeys/testplan.md" + ], + "conflict_status": "resolved", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + } + } + ], + "spec_source_policy": { + "mode": "reconcile", + "decided_by": "repository owner", + "decided_at": "2026-07-22T00:00:00+05:30", + "decision_record": "reconcile selected by repository owner on 2026-07-22; protected sources preserved and code-right conflicts resolved" + }, + "spec_sources": [ + { + "path": "README.md", + "scope": "repo", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/GETTING_STARTED.md", + "notes": "Preserved unchanged; supported setup and host API guidance was reconciled, while the limited widget list is retained as historical source wording." + }, + { + "path": "packages/node_modules/@ciscospark/*/README.md", + "scope": "reference", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/CONTRACTS.md", + "notes": "Ninety-three protected same-suffix namespace migration notices are preserved and represented by package-family Export Stability sections." + }, + { + "path": "packages/node_modules/@webex/webex-widget-base/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/webex-widget-base/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/widget-runtime-auth-spec.md", + "notes": "Preserved unchanged; current code contradicts its default two-argument construction example, so the object-shaped named constructor is canonical." + }, + { + "path": "packages/node_modules/@webex/widget-space/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-space/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/space-messaging-spec.md", + "notes": "Preserved unchanged; configuration and UI intent were checked against current props/constants." + }, + { + "path": "packages/node_modules/@webex/widget-space/events.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-space/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/space-messaging-spec.md", + "notes": "Preserved unchanged; only names present in current event constants are canonical, and obsolete notification/mention examples are flagged." + }, + { + "path": "packages/node_modules/@webex/widget-recents/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-recents/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/recents-spec.md", + "notes": "Preserved unchanged and reconciled with current configuration, filters, state, and host forms." + }, + { + "path": "packages/node_modules/@webex/widget-recents/events.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-recents/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/recents-spec.md", + "notes": "Preserved unchanged; current source event constants are canonical and obsolete notification examples are flagged." + }, + { + "path": "packages/node_modules/@webex/widget-call-history/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-call-history/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/calling-spec.md", + "notes": "Preserved unchanged and reconciled with the TypeScript entrypoint and adapter contracts." + }, + { + "path": "packages/node_modules/@webex/widget-speed-dial/src/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-speed-dial/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/calling-spec.md", + "notes": "Preserved unchanged and reconciled with current speed-dial implementation." + }, + { + "path": "packages/node_modules/@webex/widget-speed-dial/src/GenericModal/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-speed-dial/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/calling-spec.md", + "notes": "Preserved unchanged and reconciled with current modal tests/types." + }, + { + "path": "packages/node_modules/@webex/widget-demo/README.md", + "scope": "module", + "module": "packages/node_modules/@webex/widget-demo/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/widget-runtime-auth-spec.md", + "notes": "Preserved unchanged as development/demo guidance." + }, + { + "path": "test/journeys/testplan.md", + "scope": "module", + "module": "test/journeys/", + "role": "source-material", + "canonical": false, + "use_by_agents": "source-for-reconciliation", + "canonical_target": "ai-docs/modules/test-automation-spec.md", + "notes": "Preserved unchanged; every suite and scenario family is represented, while executable tests/config decide current proof." + } + ], + "spec_policy": { + "delta_grammar": { + "added": "## ADDED Requirements", + "modified": "## MODIFIED Requirements", + "removed": "## REMOVED Requirements" + }, + "require_what_and_why": true, + "require_provenance": true, + "protected_specs": [ + "README.md", + "packages/node_modules/@ciscospark/*/README.md", + "packages/node_modules/@webex/webex-widget-base/README.md", + "packages/node_modules/@webex/widget-call-history/README.md", + "packages/node_modules/@webex/widget-demo/README.md", + "packages/node_modules/@webex/widget-recents/README.md", + "packages/node_modules/@webex/widget-recents/events.md", + "packages/node_modules/@webex/widget-space/README.md", + "packages/node_modules/@webex/widget-space/events.md", + "packages/node_modules/@webex/widget-speed-dial/src/README.md", + "packages/node_modules/@webex/widget-speed-dial/src/GenericModal/README.md", + "test/journeys/testplan.md", + "AGENTS.md", + "ai-docs/ARCHITECTURE.md", + "ai-docs/CONTRACTS.md", + "ai-docs/GETTING_STARTED.md", + "ai-docs/GLOSSARY.md", + "ai-docs/REVIEW_CHECKLIST.md", + "ai-docs/RULES.md", + "ai-docs/SECURITY.md", + "ai-docs/SERVICE_STATE.md", + "ai-docs/SPEC_INDEX.md", + "ai-docs/modules/*.md", + "ai-docs/TEST_INDEX.md", + "ai-docs/SDD_BOOTSTRAP_EVIDENCE.md" + ], + "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", + "generator_runtime_source": "invocation-input", + "validator_runtime": "codex-desktop", + "validator_runtime_source": "invocation-input", + "runtime_fallback_tier": "different-runtime", + "minimum_independence": "different-runtime", + "blocking_severities": [ + "Blocking" + ], + "base_ref": "master", + "head_ref": "react-widgets-sdd-022", + "source_commit": "548a0312718835b08faf225187951a05eabc42ba", + "status": "pass", + "validated_at": "2026-09-03", + "blocking_count": 0, + "warning_count": 0, + "evidence_summary": "Independent Codex Session B Pass at 548a0312; Axis A/B 0 findings; 10 modules remain Partial" + }, + "layout": { + "sdd_root": ".sdd", + "docs_root": "ai-docs", + "agent_entry_path": "AGENTS.md", + "standing_docs_root": "ai-docs", + "spec_index_path": "ai-docs/SPEC_INDEX.md", + "module_docs_strategy": "central", + "module_docs_root": "ai-docs/modules", + "module_docs_folder_name": "ai-docs", + "template_roots": { + "canonical": ".sdd/templates", + "extensions": [ + ".sdd/templates/extensions" + ] + }, + "repo_skills_root": ".agents/skills", + "runtime_skill_entrypoints": { + "codex": ".agents/skills", + "cursor": ".cursor/skills" + }, + "contracts_strategy": "root-index-module-detail", + "test_index_path": "ai-docs/TEST_INDEX.md" + }, + "tooling": { + "sdlc_skills": { + "source_repo": "git@sqbu-github.cisco.com:WebexDevPlatform/SDLC-Skills.git", + "source_ref": "d5ec17e136514addc76df19760f1f00b05ba9c70", + "source_branch": "main", + "install_mode": "copy", + "installed_at": "2026-09-03T00:00:00+05:30", + "plugins": [ + "sdd-bootstrap@0.4.3" + ], + "template_library_version": "0.2.2", + "template_library_ref": "0aa65d93f019e5313ec2ddfac9b0c7352a2882cc" + } + }, + "coverage_ratchet": { + "enabled": true, + "waivers": [] + }, + "substrate": { + "source": "existing react-widgets repository conventions and CI configuration", + "consumed": [ + "ESLint/Babel/build configs", + "README and contribution conventions", + "CircleCI quality and release workflow" + ], + "compliance_tier": "legacy" + }, + "section_profiles": { + "repo": { + "owns_datastore": false, + "holds_client_state": true, + "components_interact": true, + "domain_data_across_components": true, + "caches_data": true, + "observability_convention": true, + "deploys_to_infra": true, + "shared_base_libs": true, + "is_monorepo": true, + "multi_platform": false, + "published_package": true, + "embedded_in_host": true, + "cross_repo_deps_material": true, + "security_arch_warranted": true + }, + "resolved_by": "codex-desktop with repository-owner approved plan and code-grounded questionnaire", + "resolved_at": "2026-07-22T00:00:00+05:30" + } +} diff --git a/.sdd/templates/component-repo/README.md b/.sdd/templates/component-repo/README.md new file mode 100644 index 000000000..bfefd3fcb --- /dev/null +++ b/.sdd/templates/component-repo/README.md @@ -0,0 +1,17 @@ +# Component Repository Templates + +Standing documentation for one component repository. In a multi-repo product, these templates apply +inside each component repo; workspace-level documentation may link across repositories, but each +component repo still owns its local standing docs. + +## Use This Folder + +Use these templates when onboarding an existing repo or refreshing its standing SDD docs. The +generated docs help agents find the repo entry point, module specs, contracts, rules, and decisions +without scanning the whole codebase. + +| Folder | Generates | Purpose | +|---|---|---| +| `standing-docs/` | Root `AGENTS.md` plus repo-level standing docs under `ai-docs/` by default | Navigation, architecture, contracts, security, service state, rules, and repo operation docs | +| `module-docs/` | Per-module specs | Canonical source-local module specs with responsibility, public surface, dependencies, requirements, data flow, sequence/class diagrams, use cases, failure paths, pitfalls, and module test strategy | +| `reference-docs/` | `ai-docs/patterns/`, `ai-docs/rules/`, `ai-docs/adr/` entries | Reference conventions, rules, and decisions | diff --git a/.sdd/templates/component-repo/module-docs/README.md b/.sdd/templates/component-repo/module-docs/README.md new file mode 100644 index 000000000..ac30d5636 --- /dev/null +++ b/.sdd/templates/component-repo/module-docs/README.md @@ -0,0 +1,28 @@ +# Per-module docs + +Per-module docs for modules, packages, services, or components inside one repository. Each module that +warrants documentation gets one canonical spec: + +## Use This Template + +Use `module-spec.template.md` when a module needs an authoritative spec or a better Partial spec. +The generated file is what future engineering work reads before changing that module. + +| Template | Generates | Purpose · when to use | +|---|---|---| +| `module-spec.template.md` | `/ai-docs/-spec.md` by default | Canonical module spec — orientation, requirements, data flow, sequence/class diagrams, use cases, business rules & invariants, concurrency, state machine, protocol/wire format, UI flow, data model, pitfalls, test approach, and coverage score metadata. Created per module during onboarding/backfill and read before modifying the module. | + +- `/ai-docs/-spec.md` is the module documentation surface. State machine, protocol/wire format, UI + flow, and data model details live as sections in that spec when they apply. +- `module-spec-quality.md` defines the completion check for generated module specs. It covers the + universal sections, detailed design, data flow, sequence diagram(s), class/component relationships, + use cases, pitfalls, error/failure paths, and module test strategy. +- Public-surface sections stay compact: summarize the endpoint/export/event, compatibility, and + migration expectations, then link to the root `CONTRACTS.md` index row and the canonical schema or + API detail source. +- Prefer `.yaml` for OpenAPI/AsyncAPI schemas unless the target repo already uses `.yml`; use `.proto`, + `.graphql`, JSON Schema, or language-native SDK API outputs when those are the natural contract + source. +- Module specs are **not** agent-entry files — the repository-level agent contract is `AGENTS.md`. + +Conventions are described in `../../README.md`. diff --git a/.sdd/templates/component-repo/module-docs/module-spec-quality.md b/.sdd/templates/component-repo/module-docs/module-spec-quality.md new file mode 100644 index 000000000..a551332c6 --- /dev/null +++ b/.sdd/templates/component-repo/module-docs/module-spec-quality.md @@ -0,0 +1,111 @@ +# Detailed Module Spec Standard + +Use this standard with `module-spec.template.md` when generating or manually writing +`/ai-docs/-spec.md`. It applies whether a team uses an automated generator +or its own manual process. + +## Required Shape + +A module spec is the canonical module document. It combines orientation, requirements, design, +behavior flows, contracts, edge cases, and module tests in one source-local file. + +Every generated module spec keeps these universal sections: + +- `Metadata` +- `Evidence Rules` +- `Source Material Register` +- `Overview` +- `Purpose / Responsibility` +- `Stack` +- `Folder / Package Structure` +- `Key Files (source of truth)` +- `Public Surface` +- `Requires (dependencies)` +- `Requirements` +- `Design Overview` +- `Data Flow` +- `Sequence Diagram(s)` +- `Class / Component Relationships` +- `Use Cases` +- `Pitfalls` +- `Test-Case Strategy (module)` +- `Traceability` + +Conditional sections are kept only when they apply to the module. When kept, they are filled with the +same level of evidence as the universal sections. + +Rendered sections follow the template order: identity/provenance, orientation, public boundary, +requirements, implementation design and flows, applicable state/data/protocol details, maintenance +edges, tests, and traceability. Reordering generated sections makes the spec harder to scan and should +be corrected before validation. + +The rendered `Metadata` table includes: `Module id`, `Source path(s)`, `Doc kind`, +`Coverage score`, `Generated from`, `generated_by / approved_by / updated_at`, and +`Validation status`. + +`Coverage score` is always present. Before measurement it is `Pending coverage assessment`; after +measurement it is the numeric percentage plus assessment date and short evidence summary. `Coverage +score` and `Validation status` must not cite `.generated/` run-record paths or other local generated +report paths. + +## Detail Expectations + +The spec should be context-efficient and complete. A future agent should be able to read the module +spec and understand the module enough to make a safe change without rediscovering the core behavior +from scratch. + +- `Overview` and `Design Overview` explain ownership, internal structure, and rationale. +- `Requirements` capture observable behavior and compatibility promises with WHAT, WHY, evidence, + tests/examples, gaps, and confidence. +- `Data Flow` names the exact transport or call style and includes a Mermaid diagram for non-trivial + modules. +- `Sequence Diagram(s)` start with a sequence inventory or coverage note. The number of diagrams is + based on major operation groups from public surfaces, use cases, events, commands, state transitions, + and async jobs. One diagram can cover multiple operations only when actors, ordering, transport, + state transition, and failure behavior are the same. A single diagram is acceptable only for a + one-operation or trivial pass-through/composition module. Include relevant error, timeout, retry, + rollback, rejected, and recovery paths. +- `Class / Component Relationships` show main objects/components and how they relate; a file list is + not enough. +- `Use Cases` are concrete actor -> steps -> outcome flows with evidence. +- `Pitfalls` capture module-specific failure modes, sharp edges, and must-not-break behavior. +- `Test-Case Strategy (module)` maps behaviors/requirements to existing tests and gaps. +- Native contract sources such as OpenAPI, AsyncAPI, proto, GraphQL, JSON Schema, SDK API reports, + typedoc, or package entry points are linked as exact detail sources. The module spec summarizes and + routes to them instead of pasting full schemas. + +Use `N/A` only when the code proves the concern cannot apply, and include the evidence path or reason. + +## Existing Docs + +When a repo already has module overview, architecture, HLD, LLD, API, or test notes, use them as +source material instead of discarding their detail. Record exact prior doc paths in +`.sdd/manifest.json` and local source-fidelity run records; the committed module spec's +`Source Material Register` summarizes source basis by category and disposition, not by old +non-canonical file names: + +- orientation content maps to `Overview`, `Purpose / Responsibility`, `Stack`, + `Folder / Package Structure`, and `Key Files`; +- architecture content maps to `Design Overview`, `Data Flow`, `Sequence Diagram(s)`, + `Class / Component Relationships`, conditional design sections, and `Pitfalls`; +- API/contract content maps to `Public Surface`, the root contract index, and linked schema/API + sources; +- behavior and test content maps to `Requirements`, `Use Cases`, and `Test-Case Strategy (module)`. + +If code evidence disproves older content, mark the conflict and do not present the old statement as +current behavior. + +## Completion Check + +Before treating a module spec as ready, check: + +- all universal sections exist; +- sections are in the source-template order; +- the rendered metadata table includes all required rows; +- retained conditional sections are present and filled; +- sequence inventory, diagrams, and failure/error/recovery paths exist for non-trivial modules; +- every requirement has WHAT, WHY, source evidence, test/example evidence or a gap, and confidence; +- public surfaces link to the root contract index and exact schema/API detail source when one exists; +- no template placeholders, generic fill text, or vague evidence anchors remain. +- migrated specs do not name old non-canonical source files as headings, appendices, or Source + Material Register rows; exact source routing remains in the manifest/local source-fidelity records. diff --git a/.sdd/templates/component-repo/module-docs/module-spec.template.md b/.sdd/templates/component-repo/module-docs/module-spec.template.md new file mode 100644 index 000000000..789c3ceba --- /dev/null +++ b/.sdd/templates/component-repo/module-docs/module-spec.template.md @@ -0,0 +1,321 @@ + + +# — SPEC + +> Start here → root [`AGENTS.md`]() (agent entry) · router [`SPEC_INDEX.md`]() · system [`ARCHITECTURE.md`](). This is the module's canonical spec: orientation, requirements, design, flows, state, protocol, UI, data, and tests. (Multi-repo: the root `AGENTS.md` may be the workspace-level one.) +> Context-efficiency: link to canonical docs — don't duplicate them. Load specs on demand per `SPEC_INDEX.md`. + +` are kept only when + the condition holds. Each section's comment gives Capture (what to write) / Avoid (the common mistake) + / Example (a generic illustration). +--> + +## Metadata + +| Field | Value | +|---|---| +| Module id | `` | +| Source path(s) | `` | +| Parent spec | the parent module's canonical spec at the manifest's module docs path, e.g. `/ai-docs/-spec.md` — or `—` when this module has no parent module | +| Doc kind | Module spec | +| Coverage score | Pending coverage assessment / `%` assessed `` | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | | +| Validation status | not-run / pass / pass-with-warnings / blocked, validator ``, assessed `` | + +Coverage score: `Pending coverage assessment` before the first report; after assessment, replace with +`<0-100%>` plus the assessment date and short evidence summary. Do not link or cite local generated +coverage or validation report paths from this committed metadata. Keep manifest coverage state outside +the rendered module doc metadata. + +## Evidence Rules +Every generated requirement below must cite concrete source evidence using `file path`. Separate source +evidence, test evidence, examples, assumptions, and gaps so validators and future agents can distinguish +truth from context. Test evidence is preferred for WHY. Commit evidence is allowed only when the +repository policy says history is reliable, and must include the commit hash. If evidence is missing or +conflicting, ask a focused discovery question before finalizing the requirement; record unresolved answers +as approved unknowns only when the human explicitly defers or does not know. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| `` | overview / architecture / API / tests / none | used / verified / reference-only / stale / conflicting / none | | + +## Overview + + + +## Purpose / Responsibility + + + + +## Tier + +**Tier:** + +## Stack + + + +## Folder / Package Structure + +``` +/src/.../ +├── / # +``` + + +## Sub-modules + +| Sub-module | Responsibility | Manifest coverage state | Spec | +|---|---|---|---| +| `/` | | | `/ai-docs/-spec.md` | + +Scope rule: every section below describes THIS module only. Behavior owned by a sub-module is recorded in +that sub-module's spec and referenced here by contract id — never duplicated, so a requirement is counted +once and at one level. + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `` | | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `.` | HTTP / SDK / event / CLI / RPC | | | | `` | `` | + +Compatibility notes: +- + +## Requires (dependencies) + + + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `-R-001` | | | `` | `` / `` / none found | | PRESENT / WEAK / APPROVED_UNKNOWN | + +Do not merge multiple unrelated behaviors into one requirement. Do not use broad evidence such as +"see source tree" or only an intake/questionnaire record; cite the implementation/test files that support +the claim. Do not record raw data/schema inventory as requirements. + +## Design Overview + + + +## Data Flow + +```mermaid + +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| `` | `` | | + +```mermaid + +``` + +## Class / Component Relationships + +```mermaid + +``` + + +## Use Cases + +- **UC-1 :** . Evidence: ``, ``. + + + + +## State Model + + + + +## Business Rules & Invariants + +- + + +## Concurrency & Reactive Flow + +- + + +## Data / Schema + +- + + +## Schema / Migration Discipline + + + + +## State Machine + +```mermaid + +``` + + +## Protocol / Wire Format + +- + + +## UI Flow + +- + + +## Data Model + +- + + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| + +## Pitfalls + +- + + +## Module Do's / Don'ts + +- DO: +- DON'T: + + +## Export Stability + + + + +## Host Integration & Theming + + + + +## Key Design Trade-off + +- + +## Test-Case Strategy (module) + + + +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| `-R-001` | `` or `None found` | | + +## Traceability + +- Repo architecture: `` · Registry: `` +- Coverage state & contracts baseline: `.sdd/manifest.json` diff --git a/.sdd/templates/component-repo/reference-docs/README.md b/.sdd/templates/component-repo/reference-docs/README.md new file mode 100644 index 000000000..fb418c09a --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/README.md @@ -0,0 +1,22 @@ +# Reference Documentation Templates + +Repeatable reference docs for a component repository. These are copied or instantiated under +`ai-docs/` when a repo needs durable conventions, enforceable rules, or architecture decisions that +future agents should load on demand. + +## Use This Folder + +Use these templates after the standing docs and module specs identify repeatable knowledge worth +promoting out of a one-off change or run record. + +| Folder | Generates | Purpose | +|---|---|---| +| `patterns/` | `ai-docs/patterns/.md` | Code-grounded conventions that are visible in real source but not fully enforced by tooling. | +| `rules/` | `ai-docs/rules/.md` | Deeper rule pages for repo-specific constraints that need rationale, examples, and enforcement detail. | +| `adr/` | `ai-docs/adr/NNNN-.md` | Append-only architecture decisions with context, rejected alternatives, consequences, and supersession. | + +Promote only durable knowledge here. Temporary notes, questionnaire output, validation reports, and +investigation transcripts stay under `.generated/`. + +Conventions (metadata header, navigation pointer, context-efficiency, `Include if:` handling, and +Capture/Avoid/Example guidance) are described in `../../README.md`. diff --git a/.sdd/templates/component-repo/reference-docs/adr/README.md b/.sdd/templates/component-repo/reference-docs/adr/README.md new file mode 100644 index 000000000..bc2163b86 --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/adr/README.md @@ -0,0 +1,19 @@ +# ai-docs/adr/ — Architecture Decision Records + +Standing, append-only record of **why** the architecture is the way it is — including the options that +were rejected. Unlike temporary generation records, ADRs live with the repo +forever, so an agent sees the reasoning behind a constraint instead of "fixing" it by accident. + +## Use ADRs For + +Use ADRs for durable architecture decisions that constrain future work. Do not use ADRs for run +notes, temporary investigation findings, or implementation task history. + +- **Fill-in shape:** `_adr-example.md` (Context · Decision · Alternatives Considered · Consequences · Revisit When). +- **Numbering:** one file per decision, `NNNN-short-title.md` (zero-padded, monotonic). +- **Immutability:** ADRs are immutable once `Accepted`. To change a decision, write a new ADR that + supersedes the old one (and set the old one's status to `Superseded by NNNN`). +- Reference ADRs from `ARCHITECTURE.md` and module specs where a decision constrains the design. + +Each ADR carries the standard metadata header, a navigation pointer, and Capture/Avoid/Example guidance. +See `../README.md` for this reference-docs area and `../../../README.md` for global conventions. diff --git a/.sdd/templates/component-repo/reference-docs/adr/_adr-example.md b/.sdd/templates/component-repo/reference-docs/adr/_adr-example.md new file mode 100644 index 000000000..209e0fe6f --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/adr/_adr-example.md @@ -0,0 +1,59 @@ +<!-- ─────────────────────────────── + Template: ADR (example) + Template-ID: adr + Generates: ai-docs/adr/NNNN-<kebab-title>.md + Description: Standing architecture decision record — context, decision, alternatives rejected, consequences. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# ADR-NNNN — <short decision title> + +> Start here → repo root [`AGENTS.md`](../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../ARCHITECTURE.md). This is a standing `ai-docs/adr/` decision record; the folder README explains numbering/supersession. +> Context-efficiency: link to canonical docs — don't duplicate them; one decision per file. + +<!-- + STANDING decision record. One decision per file; immutable once Accepted. Capture the WHY and the rejected + options so the reasoning survives. Headings are flat; sections preceded by `<!-- Include if: ... -->` are + kept only when relevant. Each section comment gives Capture / Avoid / Example. +--> + +<!-- Capture: status + date + deciders (roles) + supersession link. Avoid: editing an Accepted ADR in place — + supersede it with a new one. Example: "Status: Accepted; Deciders: architect + users TL." --> +| Field | Value | +|---|---| +| Status | Proposed / Accepted / Superseded by ADR-NNNN / Deprecated | +| Date | <YYYY-MM-DD> | +| Deciders | <roles, not just names> | +| Supersedes / Superseded by | <ADR-NNNN, or none> | +| Generated from | `adr` @ SDLC template library `0.2.2` | + +## Context +<!-- Capture: the forces — problem, constraints, what made the decision necessary, grounded in facts (file path/ + incident/requirement). Avoid: opinion with no evidence. Example: "Two services wrote `<entity>`, causing + race-condition double-posts (INC-220)." --> +<context> + +## Decision +<!-- Capture: what was decided, as a clear directive. Avoid: a vague preference. Example: "`<owner service>` is + the sole writer of `<entity>`; others call its API." --> +<decision> + +## Alternatives Considered +<!-- Capture: the rejected options + WHY (the most valuable part — stops relitigation/accidental reversal). + Avoid: listing only the chosen option. Example: "Distributed lock — adds latency + a failure mode — rejected." --> +| Alternative | Pros | Cons | Why rejected | +|---|---|---|---| + +## Consequences +<!-- Capture: what gets easier/harder, follow-on obligations, the constraint agents must now respect. Avoid: + only the upside. Example: "Agents must: never write `<entity>` rows outside `<owner service>`." --> +- **Positive:** <…> +- **Negative / cost:** <…> +- **Agents must:** <the constraint this imposes on future changes> + +<!-- Include if: the decision has a defined re-evaluation trigger --> +## Revisit When +<!-- Capture: the condition under which to reconsider. Avoid: "never". Example: "if write throughput exceeds + what a single writer can sustain." --> +- <condition under which this should be reconsidered> diff --git a/.sdd/templates/component-repo/reference-docs/patterns/README.md b/.sdd/templates/component-repo/reference-docs/patterns/README.md new file mode 100644 index 000000000..a5db0f84b --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/patterns/README.md @@ -0,0 +1,18 @@ +# ai-docs/patterns/ — repo conventions (correct vs incorrect) + +Conventions the linter doesn't catch (naming intent, error-enrichment style, event idioms), extracted +from **real source** (a convention seen in 3+ files) — never invented. + +## Use Patterns For + +Use patterns when a convention is visible in real code but not enforced by tooling. A pattern should +show correct and incorrect examples so future agents can follow the local style. + +- **Fill-in shape:** `_pattern-example.md` (When to use · Correct · Incorrect · Where it appears · Edge cases). +- **Routing:** generic patterns live directly in `ai-docs/patterns/`; language-specific ones in + `ai-docs/patterns/<language>/`. +- **Defer to the linter:** if a tool already enforces it, point to the tool instead of writing a pattern. + +Each pattern file carries the standard metadata header, a navigation pointer, and Capture/Avoid/Example +guidance. See `../README.md` for this reference-docs area and `../../../README.md` for global +conventions. diff --git a/.sdd/templates/component-repo/reference-docs/patterns/_pattern-example.md b/.sdd/templates/component-repo/reference-docs/patterns/_pattern-example.md new file mode 100644 index 000000000..5df1baacd --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/patterns/_pattern-example.md @@ -0,0 +1,49 @@ +<!-- ─────────────────────────────── + Template: Pattern (example) + Template-ID: pattern + Generates: ai-docs/patterns/<name>.md + Description: A repo convention from real code — correct vs incorrect form, with where it appears. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Pattern: <name> + +> Start here → repo root [`AGENTS.md`](../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../SPEC_INDEX.md). This is an `ai-docs/patterns/` fill-in; the folder README explains generic-vs-per-language routing. +> Context-efficiency: link to canonical docs — don't duplicate them; one small, code-grounded pattern per file. + +<!-- Example pattern file shape. Fill these from REAL code (3+ occurrences for a promoted pattern; + fewer occurrences can be a candidate note, not an enforceable pattern). Delete this example in a + real repo. Generic patterns sit here; language-specific ones go in a subfolder. + Each section comment gives Capture / Avoid / Example. --> + +## When to use +<!-- Capture: the concrete situation this pattern applies to. Avoid: "always" — give the trigger. Example: + "When emitting a domain event after a state change." --> +**When to use:** <the situation this pattern applies to> + +## Correct +<!-- Capture: the correct form copied from REAL source with a file path anchor. Avoid: an invented snippet. + Example: "// from events/emit.ts — emit(ev) wraps in the outbox." --> +```<lang> +// from <real/file/path.ext> +<correct example drawn from actual source> +``` + +## Incorrect +<!-- Capture: the common mistake + the failure it causes. Avoid: a strawman nobody writes. Example: "direct + bus.publish() — bypasses the outbox, so the event is lost if the tx rolls back." --> +```<lang> +<the common mistake> +``` +**Why wrong:** <the failure it causes> + +## Where it appears +<!-- Capture: 3+ real file:path occurrences (proves it's a real convention, not invented). Avoid: listing it + if it appears < 3 times. Example: "<module-a>/emit.ts, <module-b>/emit.ts, <module-c>/emit.ts." --> +- `<file:path>` , `<file:path>` , `<file:path>` (3+ real occurrences) + +## Edge cases / exceptions +<!-- Capture: where the pattern legitimately doesn't apply. Avoid: pretending there are none. Example: "Not for + fire-and-forget telemetry events, which skip the outbox by design." --> +- <when the pattern legitimately doesn't apply> diff --git a/.sdd/templates/component-repo/reference-docs/rules/README.md b/.sdd/templates/component-repo/reference-docs/rules/README.md new file mode 100644 index 000000000..fb6c176a9 --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/rules/README.md @@ -0,0 +1,19 @@ +# ai-docs/rules/ — deeper repo rules + +`AGENTS.md` carries the 5–10 **critical** rules; the repo-wide `RULES.md` is the digest; this folder +holds the **fuller, per-rule detail** an agent loads on demand. + +## Use Rules For + +Use rule files when a future change must consistently follow a repo-specific constraint. Keep the +short rule in `AGENTS.md` or `RULES.md`; put examples, rationale, and enforcement details here. + +- **Fill-in shape:** `_rule-example.md` (Rule · Why · How to follow · Enforced by). +- **Routing:** generic rules live directly in `ai-docs/rules/`; language-specific ones in + `ai-docs/rules/<language>/`. +- **Defer to tooling:** if a linter/CI already enforces a rule, the rule file points to that rather than + restating it. + +Each rule file carries the standard metadata header, a navigation pointer, and Capture/Avoid/Example +guidance. See `../README.md` for this reference-docs area and `../../../README.md` for global +conventions. diff --git a/.sdd/templates/component-repo/reference-docs/rules/_rule-example.md b/.sdd/templates/component-repo/reference-docs/rules/_rule-example.md new file mode 100644 index 000000000..0935ab924 --- /dev/null +++ b/.sdd/templates/component-repo/reference-docs/rules/_rule-example.md @@ -0,0 +1,42 @@ +<!-- ─────────────────────────────── + 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.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Rule: <short imperative name> + +> 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. + +<!-- + A single fill-in rule file (symmetric with patterns/_pattern-example.md). Generic rules live in + ai-docs/rules/; language-specific rules in ai-docs/rules/<language>/. One rule per file. Defer to tooling: + if a linter/CI already enforces it, point to that instead of restating it. +--> + +## Rule +<!-- Capture: the rule as one imperative sentence the agent can obey. Avoid: vague aspirations + ("write clean code") or generic best practice not specific to this repo. Example: "Wrap every + outbound network call in the shared retry helper; never call the HTTP client directly." --> +<the rule> + +## Why +<!-- Capture: the concrete reason — the incident, bug class, or convention it prevents. Avoid: "it's + best practice" with no consequence. Example: "Direct client calls bypass timeout + circuit-breaker + config and caused a cascading outage (INC-1234)." --> +<rationale> + +## How to follow +<!-- Capture: the correct way, with a real code reference (file path) or a short snippet. Avoid: an + abstract description with no example to copy. Example: "Use retryingClient.call(req) — see + src/net/retrying-client.ts." --> +<how + example reference> + +## Enforced by +<!-- Capture: the exact gate that catches a violation. Avoid: claiming enforcement that doesn't exist. + Example: "lint rule no-direct-http (errors in CI)" or "review only — no automated check yet." --> +<linter rule / CI gate / review check / "review only"> diff --git a/.sdd/templates/component-repo/standing-docs/AGENTS.template.md b/.sdd/templates/component-repo/standing-docs/AGENTS.template.md new file mode 100644 index 000000000..942b339c0 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/AGENTS.template.md @@ -0,0 +1,120 @@ +<!-- ─────────────────────────────── + Template: AGENTS.md + Template-ID: agents + Generates: AGENTS.md + Description: Agent entry contract — first file every AI agent reads (commands, rules, boundaries, routing). + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# AGENTS.md — <repo name> + +> 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. (Multi-repo: a workspace-level `AGENTS.md` may sit above this one.) +> Context-efficiency: link to canonical docs — don't duplicate them; keep this file under ~200 lines. + +<!-- + ROOT FILE 1 of 3. The agent entry point — first file every automation agent reads. KEEP UNDER + ~200 LINES — link to ai-docs/SPEC_INDEX.md and module docs instead of + duplicating. Fill every <...> from REAL repo evidence — never invent commands, paths, flags. + Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition + holds. Each section comment gives Capture (what to write) / Avoid (the common mistake) / Example. +--> + +> Cross-tool context file. Auto-loaded by AI coding agents. If a module ships its own +> `AGENTS.md` agent-entry file, that file layers on top of this root file. (A module's +> high-level design lives in the manifest-routed module spec, source-local as `<module-path>/ai-docs/<module-name>-spec.md` by default, not in an `AGENTS.md`.) + +## Repo Overview +<!-- Capture: what the repo is in 1-3 sentences + an is/is-NOT list. Avoid: marketing prose or omitting the + is-NOT list (it prevents the most common agent misunderstandings). Example: "A service/library that + owns <domain capability>. It is NOT <adjacent app/system> and does NOT own <external data>." --> +**<repo>** is <one-to-three sentences: what it is>. + +**What it is:** +- <bullet> +**What it is NOT:** +- ❌ <bullet> + +## Tech Stack +<!-- Capture: language+version, framework, build tool, test frameworks (datastores/messaging only if present). + Avoid: listing libraries the repo doesn't actually use. Example: "TypeScript 5.4, Node 20, Fastify, Vitest." --> +- <language + version>, <framework>, <build tool> +- <test frameworks; datastores/messaging only if the repo has them> + +## Architecture +<!-- Capture: ONE high-level diagram in the shape that fits the repo. Avoid: pasting full detail (that lives in + ARCHITECTURE.md). Example: a service's request→handler→store flow, or a library's consumer→public-API view. --> +``` +<one high-level diagram: a running service → layered/flow; a library → consumer→public-API→internal; a UI → component-tree→state> +``` +→ Full repo architecture & component responsibilities: **[ARCHITECTURE.md](./ai-docs/ARCHITECTURE.md)** + +## Module / Package Structure +<!-- Capture: an abbreviated tree of the key modules, one line each. Avoid: a full file dump. Example: + "src/<module-a>/ — <capability>; src/<module-b>/ — <capability>; src/<edge>/ — <entry points>." --> +``` +<abbreviated source tree of key modules; one line each> +``` +→ Per-module docs and the spec router: **[ai-docs/SPEC_INDEX.md](./ai-docs/SPEC_INDEX.md)** + +## Critical Rules +<!-- Capture: the 5-10 non-negotiables drawn from THIS repo's real review corrections; the rule LIST is + universal, the CONTENT is repo-specific. Avoid: a generic best-practice list. Example: "All <domain unit> + values use <canonical representation> — never convert implicitly." (network API → validation/authz/resilience; library → semver; + UI → a11y/design-tokens; plus the repo's real error/format/import idioms.) --> +1. **Code is the source of truth.** Never invent an API, path, event, flag, or constant — read the real file. +2. **Ask before coding.** Present a plan / Spec Summary; wait for confirmation. +3. <rule 3 — a real recurring correction from this repo's reviews> + +## Essential Commands +<!-- Capture: the real install/build/test/lint commands, mirrored from the manifest `commands` by role + (never guessed or copied from another repo). Keep this to the everyday loop; the full command + surface and test tiers live in `ai-docs/GETTING_STARTED.md` and `ai-docs/TEST_INDEX.md`. Example: + Install `npm ci` · Test `npm test` · Lint `npm run lint`. --> +| Role | Command | +|---|---| +| Install | `<cmd>` | +| Build | `<cmd>` | +| Unit test | `<cmd>` | +| Lint/format | `<cmd>` | +→ Full command surface, toolchain, and registries: **[GETTING_STARTED.md](./ai-docs/GETTING_STARTED.md)** · test tiers & coverage gate: **[TEST_INDEX.md](./ai-docs/TEST_INDEX.md)** + +## Common Gotchas +<!-- Capture: the latent-bug edges (mine incidents + tribal knowledge), each with the failure it causes. + Avoid: generic advice. Example: "<domain unit> uses <canonical representation>; passing alternate units silently corrupts results." --> +1. <gotcha — specific, with the failure it causes> + +## Pre-Commit Checklist +<!-- Capture: the must-pass checks before commit, including a repo-specific item. Avoid: a generic checklist + that doesn't reflect this repo's gates. Example: add "[ ] <repo-specific invariant> is preserved". --> +- [ ] Tests pass; coverage meets the repo bar +- [ ] Spec/docs updated in the same change (spec-currency) +- [ ] No hardcoded secrets; inputs validated +- [ ] <repo-specific item> + +<!-- Include if: the repo uses prompt-mode overrides like /adhoc or /quick --> +## Prompt Overrides (`/adhoc`, `/quick`) +<!-- Capture: the real bypass modes and what each skips. Avoid: implying correctness rules are ever bypassed. + Example: "/adhoc skips the process scaffold; validation + security rules still apply." --> +- `/adhoc` = full bypass (skip process overhead); `/quick` = partial bypass; correctness rules still apply. + +<!-- Include if: the repo's flows depend on external authenticated sources (ticket tracker / wiki / source host / design docs) --> +## External Source Access +<!-- Capture: each non-secret source/provider map the flows depend on + what to do if access is missing. + Avoid: storing tokens, guessing data a missing server would have provided, or hardcoding one vendor. + Example: ticket-tracker down → STOP and ask for configuration or pasted source content. --> +| Provider class | Source / host pattern | Preferred access | If unavailable | +|---|---|---|---| +| ticket-tracker | <host/pattern> | connector / cli / rest | STOP and ask — never guess | +| wiki/docs | <host/pattern> | connector / cli / rest / public | STOP and ask or request pasted source | +| source-host | <host/pattern> | connector / cli / rest | STOP and ask | + +<!-- Include if: the repo runs automated processes that need guaranteed compliance (strict mode) --> +## Strict Compliance Mode (automation) +<!-- Capture: when strict mode applies and how gates behave. Avoid: leaving "strict" undefined. Example: + "In CI auto-runs: load all required specs upfront; first violation halts; bounded retries." --> +Load all required specs upfront; verification gates block; stop on first violation. See ai-docs/SPEC_INDEX.md. + +--- +**SDD coverage:** this repo's per-module coverage state lives in `.sdd/manifest.json` (mirror in +`ai-docs/SPEC_INDEX.md`). Use that state to decide whether the spec is authoritative or code must be cross-checked. diff --git a/.sdd/templates/component-repo/standing-docs/ARCHITECTURE.template.md b/.sdd/templates/component-repo/standing-docs/ARCHITECTURE.template.md new file mode 100644 index 000000000..2387226d6 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/ARCHITECTURE.template.md @@ -0,0 +1,206 @@ +<!-- ─────────────────────────────── + Template: ARCHITECTURE + Template-ID: architecture + Generates: ai-docs/ARCHITECTURE.md + Description: Repo/component architecture — components, responsibilities, interactions, cross-cutting posture. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# ARCHITECTURE — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md). This is the system architecture; per-module detail lives in each manifest-routed module spec, source-local as `<module-path>/ai-docs/<module-name>-spec.md` by default. +> Context-efficiency: link to canonical docs — don't duplicate them; this loads on demand, not upfront. + +<!-- + ROOT FILE 2 of 3. Repo/component-level architecture — components, their RESPONSIBILITIES, and how they + interact. Distinct from a single module's source-local spec at + <module-path>/ai-docs/<module-name>-spec.md. Diagrams from real code, never from memory. + Headings are flat; universal sections are always present (name each for what the repo IS); sections + preceded by `<!-- Include if: ... -->` are kept only when the condition holds. Each section comment gives + Capture / Avoid / Example. +--> + +## Design Overview +<!-- Capture: the system's purpose, shape, and the key design choices that explain why it looks like this. + Avoid: a feature list with no rationale. Example: "Event-driven so intake and processing scale + independently; `<component>` is the single writer for `<domain object>`." --> +<2-3 paragraphs> + +## Component Inventory & Responsibilities +<!-- Capture: one row per component with a SINGLE clear responsibility + docs link. Avoid: + a component that "does everything" or a missing owner. Example: "`<module-a>/` — <single responsibility> — + <module-a>/ai-docs/<module-a>-spec.md". --> +| Component | Responsibility (one line) | Docs | +|---|---|---| +| `<module>/` | <what it owns> | `<module-path>/ai-docs/<module-name>-spec.md` | + +## Component Interaction +<!-- Capture: a diagram of components + the calls/events/imports between them, plus a narrative of the main + paths. Avoid: a box-only diagram with no edges. Example: "<edge> → <module-a> → <module-b>; <module-b> emits <DomainEvent>." --> +``` +<diagram: components + the calls/events/imports between them. mermaid or puml.> +``` +<Narrative: the main paths; entry points; who calls/imports whom.> + +## Execution & Flow +<!-- + Capture: the representative end-to-end flow, grounded in real code (file path); NAME the heading for what + the repo is — service → "Request / Event Flow"; per-call fn → "Invocation Flow"; library → "Init & Call + Flow"; batch → "Pipeline / Job Flow"; build tool → "Build / Generation Flow". + Avoid: a vague heading like "Flow", or describing intended (not actual) flow. + Example (service): "POST /<resource> → validate → <component>.save → emit <ResourceCreated> → 201." +--> +<the representative end-to-end flow for this repo> + +## Dependencies +<!-- Capture: each dependency, how it's used, and its failure/version handling. Avoid: omitting fallback or + version floor. Example: "<cache/store> | external | idempotency keys | fail-closed, bounded TTL." --> +| Dependency | Type (internal / external / peer) | How used | Failure / version handling | +|---|---|---|---| +| <dep> | <type> | <usage> | <fallback / peer-range / timeout> | + +<!-- Include if: the repo owns a datastore (persists its own data) [condition-id: repo.owns_datastore] --> +### Data & Schema +<!-- Capture: the datastores, key schemas, cache patterns, migration discipline. Avoid: data owned elsewhere. + Example: "<datastore> `<entity_table>`, `<event_table>`; migrations expand→migrate→contract." --> +- <datastores, key schemas, cache patterns, migration discipline> + +<!-- Include if: the repo holds client-side state (UI store / in-memory session model) [condition-id: repo.holds_client_state] --> +### State Model +<!-- Capture: the state shape (store/slices) and what triggers transitions. Avoid: documenting server data. + Example: "<flow> slice {step, data}; NEXT_STEP advances; RESET on success." --> +- <the state shape and what triggers transitions> + +## Cross-Cutting Concerns +<!-- Capture: the security + observability posture every change must respect. Avoid: "TBD" — every repo has + some posture. Example: "Security: tokens checked at the boundary, secrets from managed storage. Observability: structured logs + + correlation id; request/error/duration metrics per route." --> +- **Security:** <authn/authz model, secret/token handling, sensitive-data rules> +- **Observability:** <logs/metrics/traces + correlation ids for a service; telemetry/error reporting for a client artifact> + +## Non-Functional Posture +<!-- + Capture: the quality/scale expectations, NAMING the heading for what the repo is — service → "Scale & SLOs"; + library → "Footprint & Compatibility"; UI app → "Performance & Accessibility"; CLI → "Performance & + Footprint"; batch → "Throughput & Cost". Avoid: copying numbers from another system. + Example (service): "Scale & SLOs — <target throughput>, p99 < <latency bound>, horizontal autoscale." +--> +<the non-functional posture in the form that fits this repo> + +<!-- ===== Conditional extras — keep a section only when its Include-if condition holds ===== --> + +<!-- Include if: components/services call each other or exchange events (most non-trivial repos) [condition-id: repo.components_interact] --> +## Dependency / Interaction Topology +<!-- Capture: the who-calls-whom call graph AND the event topology, as a first-class view. Avoid: burying it + in prose. Example: graph edges "<edge>→<module-a> (call)", "<module-b>→<bus> <DomainEvent> (event)". --> +``` +<call + event graph: nodes are components/services, edges are calls (sync) and events (async)> +``` +| From | To | Kind (call / event) | Purpose | +|---|---|---|---| + +<!-- Include if: the repo owns domain data spread across components [condition-id: repo.domain_data_across_components] --> +## Object / Data Ownership +<!-- Capture: each domain object → the single component that may write it. Avoid: two components writing the + same object. Example: "<DomainObject> → <writer component>; read by <reader component>." --> +| Domain object | System-of-record (owning component) | Read by | +|---|---|---| + +<!-- Include if: the repo caches data [condition-id: repo.caches_data] --> +## Caching Catalog +<!-- Capture: each cache, backend, what it holds, TTL, invalidation. Avoid: a cache with no invalidation rule. + Example: "<resource-cache> | <cache backend> | <resource by key> | 60s | invalidate on <ResourceChanged>." --> +| Cache | Backend | What it holds | TTL | Invalidation trigger | +|---|---|---|---|---| + +<!-- Include if: the repo has a logging/metrics/audit convention worth standardizing [condition-id: repo.observability_convention] --> +## Observability Patterns +<!-- Capture: the logging format + id propagation, metric naming + key signals, what is audited. Avoid: + logging secrets/PII (see SECURITY). Example: "JSON logs w/ correlation_id; metrics <prefix>_*; audit on privileged changes." --> +- **Logging:** <structured format, correlation/request id propagation, what is never logged> +- **Metrics:** <naming convention, key signals, where dashboards live> +- **Audit:** <what is audited and where> + +<!-- Include if: the repo deploys to / depends on infrastructure [condition-id: repo.deploys_to_infra] --> +## Infrastructure Matrix +<!-- Capture: the datastores, messaging, and cloud services actually in use. Avoid: aspirational infra. + Example: "Datastores: <db>; Messaging: <broker>; Cloud/platform: <service>." --> +| Category | In use | Notes | +|---|---|---| +| Datastores | <db(s)> | | +| Messaging / streaming | <broker(s)> | | +| Cloud / platform services | <services> | | + +<!-- Include if: the repo inherits a shared/base library stack every module uses [condition-id: repo.shared_base_libs] --> +## Shared / Base Libraries +<!-- Capture: the shared libs every module inherits + version floor. Avoid: listing app deps here. Example: + "<shared-logging> (^3) — structured logger + correlation-id filter." --> +| Library | What every module inherits from it | Version floor | +|---|---|---| + +<!-- Include if: the repo is a monorepo (multiple packages in one tree) [condition-id: repo.is_monorepo] --> +## Package Map & Inter-Package Dependencies +<!-- Capture: workspace globs, package→responsibility (+ public/internal visibility), inter-package graph, + version-sync rule. Avoid: hidden cyclic deps. Example: "packages/* ; <adapter> depends on <core>; <core> depends on none." --> +- Workspace tooling and the workspace globs. +- Package → responsibility table, with a **visibility** column (public / internal) where it applies. +- Inter-package dependency graph (incl. workspace-internal deps) + the release/version-sync rule. +- Per-package notes where a package is a different kind than the repo (e.g. a library package next to an app). + +<!-- Include if: the repo targets multiple platforms (mobile / desktop / embedded / cross-platform) [condition-id: repo.multi_platform] --> +## Platform Matrix +<!-- Capture: per-platform the shared-core vs per-platform split + entry/build. Avoid: assuming one platform's + build works for all. Example: "iOS | shared core + SwiftUI shell | xcodebuild." --> +| Platform | Shared core vs per-platform | Entry / build | Notes | +|---|---|---|---| +| <platform> | <split> | <build> | | + +<!-- Include if: the repo is published/consumed as a package (npm/Maven/PyPI/etc.) [condition-id: repo.published_package] --> +## Release & Versioning +<!-- Capture: publish target, semver rules, deprecation policy, changelog obligation. Avoid: breaking changes + without a major bump. Example: "Published to the internal npm registry; semver; 1 minor deprecation window." --> +- Publish target; semver rules; deprecation policy; consumer-facing changelog obligation. + +<!-- Include if: the repo is embedded into a host application (widget / micro-frontend / extension) [condition-id: repo.embedded_in_host] --> +## Host Integration & Theming +<!-- Capture: how a host mounts/embeds it, required providers, pinned theme/peer versions. Avoid: assuming the + host's framework version. Example: "Mounts as <pay-widget>; needs ThemeProvider; peer react ^18." --> +- How a host application mounts/embeds this; required host providers; pinned design-token/theme versions; + peer/host-framework version floors. + +<!-- Include if: cross-repo dependencies are material (consumed/published artifacts usually qualify; common in topology A) [condition-id: repo.cross_repo_deps_material] --> +## Cross-Repo Dependency Graph +<!-- Capture: the repos this one consumes or is consumed by + what's exchanged. Avoid: an unscoped "see other + repos". Example: "Consumes shared-contracts (event schemas); consumed by reporting-svc." --> +- **Internal (same org):** <repos + what's exchanged> +- **Cross-project:** <repos + contract> +- **External read-only:** <repos referenced, not modified> +- **External services:** <APIs/backends called> + +<!-- Include if: the security architecture warrants its own view (trust boundaries, identity flow) [condition-id: repo.security_arch_warranted] --> +## Security Architecture +<!-- Capture: trust boundaries, token/identity flow, encryption at rest/in transit. Avoid: duplicating + SECURITY.md — keep this to the architectural view. Example: "mTLS between services; tokens minted at edge." --> +<trust boundaries, token/identity flow, encryption at rest/in transit> + +--- +→ Per-module orientation and detailed design live in each manifest-routed module spec, source-local as `<module-path>/ai-docs/<module-name>-spec.md` by default. Routing: `SPEC_INDEX.md`. + +## Architecture Reference Links +<!-- Capture: links to the local decision, pattern, and rule docs that explain or constrain this + architecture. Avoid: restating those docs here; summarize only why architecture readers should + consult them. --> +| Reference | Location | When to read | +|---|---|---| +| Architecture decisions | `adr/` | To understand why major design choices were made and what alternatives were rejected | +| Repo patterns | `patterns/` | To follow established implementation conventions reflected in this architecture | +| Enforceable rules | `RULES.md` + `rules/` | To understand constraints every architecture-affecting change must obey | + +## WS6 References +<!-- Capture: links to WS6 specs, platform architecture, shared service architecture, or enterprise + architecture documents when they exist. Avoid: copying WS6 content into this component repo doc; + link to the authoritative WS6 source and summarize only the local implication. --> +| WS6 artifact | Relevance to this repo | Link | +|---|---|---| +| <WS6 spec / architecture doc> | <why this repo should read it> | <url/path when available> | diff --git a/.sdd/templates/component-repo/standing-docs/CONTRACTS.template.md b/.sdd/templates/component-repo/standing-docs/CONTRACTS.template.md new file mode 100644 index 000000000..2afd82cde --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/CONTRACTS.template.md @@ -0,0 +1,90 @@ +<!-- ─────────────────────────────── + Template: Contracts Catalog + Template-ID: contracts + Generates: ai-docs/CONTRACTS.md + Description: Standing as-built public-surface catalog (Provides/Requires) + compatibility policy. + Library ver: 0.2.2 + Last updated: 2026-07-11 +─────────────────────────────── --> + +# Contracts Catalog — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this root contract index; detailed contracts live with owning modules or canonical schema files. Machine source `.sdd/manifest.json`. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc — the repo's stable public-surface index (the manifest's Provides/Requires promoted + to a doc). Prevents re-creating an existing endpoint/event or breaking a consumer. Detailed schemas and + operation-level behavior belong in native schema/API files or module-local specs. + Keep this file compact: one row per public surface, with owner, compatibility, and a detail link. Headings are flat; + sections preceded by `<!-- Include if: ... -->` are kept only when the + condition holds. Each section comment gives Capture / Avoid / Example. Fill from real code (file path). +--> + +> Read before adding any public-facing surface — check here first. Machine source of truth: `.sdd/manifest.json`. +> Schema convention: prefer `.yaml` for OpenAPI/AsyncAPI unless this repo already standardizes on `.yml`; use +> `.proto`, `.graphql`, JSON Schema, or language-native SDK API outputs when those are the natural source. +> Do not inline large schemas here. + +<!-- Include if: the repo exposes a network/HTTP API --> +### API Endpoints +<!-- Capture: each public route — stable id, owner module, method+path, purpose, auth/scope, compatibility, + schema/detail link, where defined. Avoid: listing internal routes as public or pasting full schemas. + Example: "billing.invoice.get | billing/ | GET /<resource>/{id} | fetch <resource> | + scope read:<resource> | stable; additive fields only | openapi.yaml#/paths/... | routes/<resource>.ts." --> +| Contract ID | Owner module | Method + path | Purpose | Auth / scope | Compatibility / deprecation | Schema / detail link | Defined at | +|---|---|---|---|---|---|---|---| + +<!-- Include if: the repo is imported as a library/package --> +### Exported API & Types +<!-- Capture: each public symbol + owner package/module + signature + semver stability + detail link. Avoid: + documenting non-exported internals or inventing custom YAML for SDK APIs. Example: + "sdk.client.create | packages/sdk | createClient(opts): Client | stable semver surface | + api-report.md#createClient | index.ts." --> +| Contract ID | Owner module/package | Symbol | Signature | Stability / deprecation | Schema / detail link | Defined at | +|---|---|---|---|---|---|---| + +<!-- Include if: the repo publishes or consumes events/messages --> +### Events +<!-- Capture: each event — owner module, direction, payload schema link, delivery guarantees, deprecation. + Avoid: omitting ordering/delivery semantics or pasting large payloads. Example: + "billing.invoice.created | billing/ | publish | asyncapi.yaml#/channels/... | at-least-once | + additive fields only | events/<resource>.ts." --> +| Contract ID | Owner module | Event / topic | Direction (publish/consume) | Payload schema link | Delivery guarantees | Compatibility / deprecation | Defined at | +|---|---|---|---|---|---|---|---| + +<!-- Include if: the repo exposes a CLI --> +### Commands & Flags +<!-- Capture: each command, owner module, args/flags, exit codes, compatibility, where defined. Avoid: + undocumented exit codes. Example: "sync.job | cli/ | sync --since DATE | exit 0 ok / 2 bad-args | + stable flags | cli/sync.ts." --> +| Contract ID | Owner module | Command | Args / flags | Exit codes | Compatibility / deprecation | Defined at | +|---|---|---|---|---|---|---| + +## Requires — what this repo depends on +<!-- Capture: each outward dependency's consumed contract + availability assumption + fallback + version floor. + Avoid: omitting the fallback. Example: "rates-svc | GET /rates | assume 99.9% | cache last-good | v2." --> +| Dependency (service / package / datastore) | What is consumed | Schema / detail link | Availability assumption | Fallback on failure | Version floor | +|---|---|---|---|---|---| + +## Compatibility & Deprecation Policy +<!-- Capture: the rule for changing the above without breaking consumers. Avoid: a breaking change with no + version bump/window. Example: "No breaking change without a major bump + 1-release deprecation window." --> +- **Breaking-change rule:** <e.g. no breaking change without a version bump + consumer transition note + deprecation window> +- **Deprecation:** <how a surface is marked deprecated and for how long> + +<!-- Include if: a non-trivial interface needs full schema/error detail beyond this catalog --> +## Detailed Interface Docs +<!-- Capture: links to native schema/API files and per-interface docs (schema, error catalog, backward-compat). + Avoid: inlining huge schemas here. Example: "<Resource API> → openapi.yaml#/paths/... and + design/contracts/<resource-api>.md." --> +- Large/critical interfaces link to their canonical schema/API source and, when needed, a + module-local contract doc. + +## Maintenance +<!-- Capture: the rule that keeps the catalog + manifest in sync. Avoid: updating one and not the other. --> +- When a public surface is added/changed/removed, update this catalog, the owning module spec summary, + any canonical schema/API detail source, and `.sdd/manifest.json` in the same change. +- For incompatible changes, include the consumer transition/deprecation plan in the owning contract detail and + summarize it in the Compatibility / deprecation column. +- Cross-reference: domain terms → `GLOSSARY.md`; entities → `DATA_MODEL.md`. diff --git a/.sdd/templates/component-repo/standing-docs/DATA_MODEL.template.md b/.sdd/templates/component-repo/standing-docs/DATA_MODEL.template.md new file mode 100644 index 000000000..07d21ecd2 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/DATA_MODEL.template.md @@ -0,0 +1,68 @@ +<!-- ─────────────────────────────── + Template: Data Model + Template-ID: data-model + Generates: ai-docs/DATA_MODEL.md + Description: Repo-wide entities, system-of-record ownership, relationships, and migration discipline. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Data Model — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc; related: `GLOSSARY.md`, `CONTRACTS.md`, `SECURITY.md`. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc — the repo-wide data model: entities owned, the system-of-record for each, schema/ + relationships, and migration discipline. Only for repos that own persistent data. Headings are flat; + sections preceded by `<!-- Include if: ... -->` are kept only when the condition holds. Each section + comment gives Capture / Avoid / Example. Fill from the real schema/migrations (file path). +--> + +> Read before changing any persisted shape. Respect ownership and the migration discipline below. + +## Entity Catalog +<!-- Capture: each persisted entity → meaning, the owning module (single writer), its table, where defined. + Avoid: an entity with two writers, or guessing the store. Example: "<Entity> | <domain meaning> | <module> + | <table> | db/schema.sql." --> +| Entity | What it represents | System-of-record (owning module) | Stored in (table/collection) | Defined at | +|---|---|---|---|---| +| `<entity>` | <meaning> | `<module>` | `<table>` | `<file path>` | + +## Relationships +<!-- Capture: how entities relate (1:1/1:N/N:M), ideally a diagram. Avoid: implied relationships with no FK/ + join documented. Example: "<EntityA> 1:N <EntityB> (<entity_b>.<entity_a_id>)." --> +``` +<ER-style diagram or relationship list> +``` + +## Ownership & Access Rules +<!-- Capture: who may write vs read each entity + the access path. Avoid: cross-module direct writes that break + invariants. Example: "<Entity> | write: <owner module> only | read: <reader module> | via <Repository>." --> +| Entity | May write | May read | Access path (API/repo layer) | +|---|---|---|---| + +<!-- Include if: the repo uses caching over its data --> +## Caching +<!-- Capture: each cache over the data — backend, key, TTL, invalidation. Avoid: a cache with no invalidation. + Example: "<entity-cache> | <cache backend> | <entity>:{id} | 30s | bust on owner write." --> +| Cached data | Backend | Key | TTL | Invalidation trigger | +|---|---|---|---|---| + +## Migration Discipline +<!-- Capture: where migrations live, the ordering/expand-contract rule, the never-edit-shipped rule. Avoid: + destructive in-place migrations. Example: "db/migrations/NNNN_*.sql; expand→migrate→contract; additive first." --> +- Migrations are the source of truth for schema; <where they live + numbering/ordering rule>. +- Backward-compatible by default (expand → migrate → contract); <true-up/backfill expectation>. +- Never edit a shipped migration in place; add a new one. + +<!-- Include if: some data is sensitive (PII/secret/regulated) --> +## Sensitive Data +<!-- Capture: which entities hold sensitive data + encryption/retention/deletion per `SECURITY.md`. Avoid: + storing PII unencrypted or with no retention rule. Example: "Customer.email — encrypted; delete 30d after close." --> +- Classify per `SECURITY.md`; note encryption-at-rest, retention, and deletion obligations per entity. + +## Maintenance +<!-- Capture: the rule that keeps this current. Avoid: schema changes without a doc update. --> +- New/changed entity or migration → update this doc in the same change. +- Cross-reference: terms → `GLOSSARY.md`; exposed data contracts → `CONTRACTS.md`. diff --git a/.sdd/templates/component-repo/standing-docs/GETTING_STARTED.template.md b/.sdd/templates/component-repo/standing-docs/GETTING_STARTED.template.md new file mode 100644 index 000000000..9aada4f2b --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/GETTING_STARTED.template.md @@ -0,0 +1,111 @@ +<!-- ─────────────────────────────── + Template: Getting Started + Template-ID: getting-started + Generates: ai-docs/GETTING_STARTED.md + Description: Clone/build/run loop, toolchain, config/secrets, artifact registries, and multi-repo workspace layout. + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# Getting Started — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc to get a build/test loop running. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc — zero to a running build/test loop, plus the multi-repo workspace layout. Fill every + command from the real build config (file path) — never guess. Headings are flat; sections preceded by + `<!-- Include if: ... -->` are kept only when the condition holds. Each section comment gives Capture / + Avoid / Example. +--> + +## Prerequisites + +### Toolchain +<!-- Capture: the exact language runtimes and build tools a contributor must install before any command + runs, one row each, mirrored from the manifest `toolchain` block. Read versions from committed config + (.nvmrc, .tool-versions, engines, compiler-release, Dockerfile); if a required version is not pinned + in the repo, capture it via the onboarding questionnaire rather than guessing. Avoid: "install Node" + with no version, or a version invented from memory. Example: JDK 21 · Maven 3.8+ · Docker. --> +| Tool | Version | Where it's pinned | +|---|---|---| +| `<tool>` | `<version>` | `<source file, or "declared during onboarding">` | + +### Access +<!-- Capture: the accounts/network access needed before anything runs — registries, VPN/egress, secret + store. Name registries here; the exact hosts and auth variable names live under Configuration & Secrets. + Avoid: omitting an internal registry or egress requirement the build depends on. Example: "read access + to the internal Maven registry; outbound egress to the artifact host; VPN for staging." --> +- <required registries / egress / VPN / accounts — or "none"> + +## Clone & Install +<!-- Capture: the real clone + install commands. Avoid: a generic `npm install` if the repo pins `npm ci`. + Example: "git clone <url> && cd <repo> && pnpm install --frozen-lockfile". --> +```bash +<git clone ...> +<install command — from the real build config> +``` + +## Build / Run / Test +<!-- Capture: the core loop copied from the manifest `commands`, one row per command role so a reader + (or a builder/tester workflow) runs the right command for each job. Avoid: inventing commands or + collapsing distinct roles (a package build without tests is not the full build). Include only the + roles the repo actually defines. Example: Install `mvn -N …` · Build `mvn verify` · Unit test + `mvn -pl '!test' test` · Integration test `mvn -pl test verify` · Coverage `mvn jacoco:check`. --> +| Role | Command | +|---|---| +| Install | `<cmd>` | +| Build (full, with tests) | `<cmd>` | +| Package (build without tests) | `<cmd>` | +| Run (local) | `<cmd>` | +| Unit test | `<cmd>` | +| Integration test | `<cmd>` | +| Coverage check | `<cmd>` | +| Lint / format | `<cmd>` | + +## First-Run Verification +<!-- Capture: one concrete check that proves the setup works. Avoid: "it should work". Example: "curl + localhost:8080/health → 200 {\"status\":\"ok\"}." --> +- <how to confirm it's working> + +<!-- Include if: the repo needs local config / env vars / secrets to run --> +## Configuration & Secrets +<!-- Capture: required config/env + where secrets come from (never hardcode). Avoid: committing a real .env. + Example: "Copy .env.example; fill DB_URL; secrets via the secret manager." --> +- Required config/env: <list>. Obtain secrets from <secret store> — never hardcode (see `SECURITY.md`). + +<!-- Include if: the build resolves dependencies from an external or authenticated registry --> +### Artifact Registries +<!-- Capture: each external artifact source the build resolves from, mirrored from the manifest + `registries` block: name, host, the settings file that points the build at it, and the NAMES of the + auth environment variables. Record names and hosts only — NEVER the credential values (see + `SECURITY.md`); the values are provided by the environment/pipeline. Avoid: pasting a token, omitting + the registry host, or dropping the auth variable names a fresh clone needs to authenticate. + Example: "internal-maven | artifacts.example.com | config/maven/settings.xml | ARTIFACTORY_USER, + ARTIFACTORY_TOKEN". --> +| Registry | Host | Settings file | Auth env-var names (values NOT stored) | +|---|---|---|---| +| `<name>` | `<host>` | `<settings file>` | `<VAR_NAME_1>, <VAR_NAME_2>` | + +<!-- Include if: topology A — the product spans multiple repositories --> +## Multi-Repo Workspace Layout +<!-- Capture: how the sibling repos are laid out locally so cross-repo work resolves. Avoid: assuming one clone + is enough for a multi-repo product. + Verify against the team's real workspace convention when available. --> +``` +<workspace-root>/ + <owner>/<repo-a>/ + <owner>/<repo-b>/ + <shared spec or workspace-level AGENTS.md, if used> +``` +- Related repos and what each provides: see `ARCHITECTURE.md` (Cross-Repo Dependency Graph) and the workspace-level `AGENTS.md`. + +<!-- Include if: the repo has a containerized or scripted dev environment --> +## Dev Environment +<!-- Capture: the devcontainer/compose/make target that brings up dependencies. Avoid: manual multi-step setup + when a one-command env exists. Example: "make dev brings up <datastore> + the app via docker compose." --> +- <devcontainer / compose / make target that brings up dependencies> + +## Where to Go Next +- Agent entry: `../AGENTS.md` · System shape: `ARCHITECTURE.md` · Routing: `SPEC_INDEX.md` +- Conventions: `patterns/` + `rules/` (and `RULES.md`). diff --git a/.sdd/templates/component-repo/standing-docs/GLOSSARY.template.md b/.sdd/templates/component-repo/standing-docs/GLOSSARY.template.md new file mode 100644 index 000000000..a2ba6d3ec --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/GLOSSARY.template.md @@ -0,0 +1,57 @@ +<!-- ─────────────────────────────── + Template: Glossary + Template-ID: glossary + Generates: ai-docs/GLOSSARY.md + Description: Ubiquitous language — domain term → definition → authoritative code location. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Glossary — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc; related: `DATA_MODEL.md`, `CONTRACTS.md`. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc. The repo's ubiquitous language: each domain term → a precise definition → where it + lives in code. Prevents renamed concepts and invented synonyms. Headings are flat; sections preceded by + `<!-- Include if: ... -->` are kept only when the condition holds. Each section comment gives Capture / + Avoid / Example. Fill from real code (file path) — never invent a term that isn't used. +--> + +> Read this before naming anything. Use the canonical name exactly; never introduce a synonym. Find a term +> in code that isn't here? Add it rather than guessing its meaning. + +## Domain Terms +<!-- Capture: each term that has a SPECIFIC meaning here → definition + the type/file that owns it + synonyms + to avoid. Avoid: dictionary definitions or terms with no code anchor. Example: "<Term> | <precise repo meaning> + | <module>/<file>.ts | not '<ambiguous synonym>'." --> +| Term | Definition (one or two sentences) | Authoritative location (file/type) | Notes / synonyms to avoid | +|---|---|---|---| +| `<term>` | <what it means here> | `<file path>` | <"do not call it X"> | + +## Abbreviations & Acronyms +<!-- Capture: every abbreviation the codebase uses, expanded + its meaning here. Avoid: leaving an acronym + ambiguous across domains. Example: "<ABC> — <Full Expansion> (not '<other possible expansion>')." --> +| Abbreviation | Expansion | Meaning in this repo | +|---|---|---| + +<!-- Include if: the same word means different things in different modules/bounded contexts --> +## Context-Specific Meanings +<!-- Capture: where a term legitimately differs by area, each meaning + its boundary. Avoid: merging the two + meanings. Example: "'<Term>' = <meaning A> in <module-a>/, but = <meaning B> in <module-b>/." --> +| Term | Context / module | Meaning here | +|---|---|---| + +<!-- Include if: the repo has deprecated/renamed concepts still present in older code --> +## Deprecated / Renamed Terms +<!-- Capture: old term → current term, why renamed, where the old one still appears. Avoid: silently dropping + the old name (readers still hit it in old code). Example: "'<OldTerm>' → '<CurrentTerm>'; renamed in <version>; still in <legacy-path>/." --> +| Old term | Current term | Why renamed | Still appears in | +|---|---|---|---| + +## Maintenance +<!-- Capture: the rule that keeps this current. Avoid: letting the glossary rot. Example: "New entity/event/ + state → add a term here in the same change." --> +- When a new domain concept is introduced (new entity, event, state), add it here in the same change. +- Cross-reference: data entities → `DATA_MODEL.md`; public-surface terms → `CONTRACTS.md`. diff --git a/.sdd/templates/component-repo/standing-docs/README.md b/.sdd/templates/component-repo/standing-docs/README.md new file mode 100644 index 000000000..aae9439b9 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/README.md @@ -0,0 +1,58 @@ +# Repository-Level Templates + +Standing docs generated for the component repository. `AGENTS.md` stays at the repository root for +tool auto-discovery; other standing docs default under root `ai-docs/`. These are written during onboarding and +maintained over time. They let an agent or human find the entry point, router, rules, contracts, and +domain language without guessing. `SPEC_INDEX.md` lives at `ai-docs/SPEC_INDEX.md` by default. + +## Generated Order + +The practical load order is: + +```text +AGENTS.md + -> ai-docs/SPEC_INDEX.md + -> ai-docs/ARCHITECTURE.md + -> module specs and focused standing docs as needed +``` + +Edit these templates only when the repo-level standing-doc shape changes. For module behavior, use +`../module-docs/`. + +When generated, each standing doc should instantiate its source template in order. Keep universal +headings, apply Include-if sections from the repo profile, and fill retained sections with concrete +repo-grounded detail. Do not replace a standing doc with a short summary because the details feel +large; route detailed module behavior to source-local module specs and exact contract/schema sources. + +| Template | Generates | Purpose · when to use | +|---|---|---| +| `AGENTS.template.md` | `AGENTS.md` | The agent entry contract — first file read. Commands, critical rules, boundaries, routing. Keep < ~200 lines. | +| `ARCHITECTURE.template.md` | `ai-docs/ARCHITECTURE.md` | System architecture: components + interactions + topology/ownership/caching/observability/infra (Include-if). | +| `SPEC_INDEX.template.md` | `ai-docs/SPEC_INDEX.md` | The router: which docs to load per task and the canonical module registry. Loaded second. | +| `GLOSSARY.template.md` | `ai-docs/GLOSSARY.md` | Ubiquitous language: term → definition → authoritative code location. | +| `SECURITY.template.md` | `ai-docs/SECURITY.md` | Standing security posture: trust boundaries, authn/authz, secret handling, data classification. | +| `CONTRACTS.template.md` | `ai-docs/CONTRACTS.md` | Root index of as-built public-surface contracts; detailed contracts live near owning modules or native schema sources. | +| `DATA_MODEL.template.md` | `ai-docs/DATA_MODEL.md` | Entities, system-of-record ownership, relationships, migration discipline (if the repo owns data). | +| `GETTING_STARTED.template.md` | `ai-docs/GETTING_STARTED.md` | Clone/build/run loop, config/secrets, multi-repo workspace layout. | +| `RULES.template.md` | `ai-docs/RULES.md` | Enforceable do/don't beyond AGENTS: coverage map, autonomy, naming, logging, errors, testing, drift, secrets. | +| `REVIEW_CHECKLIST.template.md` | `ai-docs/REVIEW_CHECKLIST.md` | The 6-core + 4-coverage + 3-cross-cutting review-check catalog (Review & Merge). | +| `SERVICE_STATE.template.md` | `ai-docs/SERVICE_STATE.md` | Living as-built registry — read first to avoid duplicating/breaking a surface. | +| `TEST_INDEX.template.md` | `ai-docs/TEST_INDEX.md` | Repo-wide test surface: tiers, commands by role, directories, frameworks, coverage gate — routes to where cases live (module specs + feature test strategy); does not duplicate them. | + +## Contract Schema Convention + +Use `CONTRACTS.md` as the repo-wide index of public surfaces. Keep exact interface schemas in the +repo's native contract format and link to them from the index and module specs. + +- Prefer `.yaml` for OpenAPI and AsyncAPI files unless the target repo already standardizes on `.yml`. +- Use `.proto` for gRPC, `.graphql` for GraphQL, and JSON Schema for standalone payload schemas. +- Use language-native SDK API surfaces for SDKs, such as exported declarations, generated API reports, + or the package entry point, instead of inventing a custom YAML contract. +- Do not paste full schemas into module specs. Module specs summarize the surface, compatibility, and + migration rules; schema/detail files define the exact contract. + +**Topology note:** in a multi-repo workspace, a workspace-level entry file may span repositories, with +this component repo's `AGENTS.md` layering beneath it. + +Conventions (metadata header · navigation pointer · context-efficiency · flat headings + `Include if:` +· Capture/Avoid/Example) are described in `../../README.md`. diff --git a/.sdd/templates/component-repo/standing-docs/REVIEW_CHECKLIST.template.md b/.sdd/templates/component-repo/standing-docs/REVIEW_CHECKLIST.template.md new file mode 100644 index 000000000..fa8999f92 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/REVIEW_CHECKLIST.template.md @@ -0,0 +1,69 @@ +<!-- ─────────────────────────────── + Template: Review-Check Catalog + Template-ID: review-checklist + Generates: ai-docs/REVIEW_CHECKLIST.md + Description: The review checks — 6 core + 4 coverage-conditional + 3 cross-cutting — selected by manifest coverage state. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Review-Check Catalog — <repo name> + +> 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. + +<!-- + STANDING checklist the repo's change reviews run against (Review & Merge). 6 core (always) + 4 + coverage-conditional (by the touched module's manifest coverage state) + 3 cross-cutting (higher risk). Run by a + DIFFERENT runtime than the generator; findings remain review artifacts until explicitly approved for + publication. Headings are flat. Each section comment gives Capture / Avoid / Example. +--> + +> 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) +<!-- Capture: run all six on every change; record a finding per failure with file path + a concrete fix. Avoid: + vague findings ("improve error handling"). Example: "C3 fail — spec says verifier 43-128, code enforces + 32-128 @auth.ts." --> +| # | Check | What it verifies | Severity if it fails | +|---|---|---|---| +| 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 (run by the touched module's manifest coverage state) +<!-- Capture: add these when the manifest coverage state requires code cross-check or characterization + (or a guarantee is removed/modified). Avoid: skipping the regression guard when changing a weakly covered module. Example: "K1 applies — no characterization + baseline exists → Blocking until one is added." --> +| # | Check | When it applies | What it verifies | Severity | +|---|---|---|---|---| +| 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 (apply at higher risk / autonomy) +<!-- Capture: add these for high-risk or higher-autonomy changes. Avoid: merging an autonomous change that the + generator also validated. Example: "X1 — validator ran on the same runtime as the generator → Blocking." --> +| # | Check | What it verifies | Severity | +|---|---|---|---| +| 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 +<!-- Capture: 6 core always + the coverage-conditional checks matching the touched modules' manifest coverage state + cross-cutting + for high-risk. Avoid: running only the core set on a risky migration. --> +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 +<!-- Capture: a compliance matrix (check → pass/warn/fail w/ file path) + severity-sorted findings + verdict; + draft only. Avoid: auto-posting to the PR. Example: verdict "Blocked — 1 Blocking (C3)". --> +- A compliance matrix + severity-sorted findings + a verdict (Pass / Pass-with-warnings / Blocked). + Draft only; a human posts. diff --git a/.sdd/templates/component-repo/standing-docs/RULES.template.md b/.sdd/templates/component-repo/standing-docs/RULES.template.md new file mode 100644 index 000000000..f7c71cf76 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/RULES.template.md @@ -0,0 +1,96 @@ +<!-- ─────────────────────────────── + Template: RULES + Template-ID: rules + Generates: ai-docs/RULES.md + Description: Enforceable do/don't beyond AGENTS — coverage, autonomy, naming, logging, errors, testing, security, drift, secrets. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Rules — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry, carries the critical rules) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc; per-language detail in `rules/<language>/`. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc — the enforceable do/don't rules beyond AGENTS.md's few critical ones. Every rule is + EXTRACTED from this repo's real reviews/conventions (file path), not generic best practice; defer to the + linter where it already enforces. Headings are flat; sections preceded by `<!-- Include if: ... -->` are + kept only when the condition holds. Each section comment gives Capture / Avoid / Example. +--> + +> These rules are checkable. Every MUST rule records its source requirement/risk, verification path, +> severity, and owner. Name the tool where one enforces a rule; say "review only" plus why otherwise. + +## Coverage Map (which docs/specs to trust) +<!-- Capture: per-module coverage state (mirrored from the manifest) + what it implies. Avoid: drifting from the + manifest. Example: "<module-a>/ manifest state → trust level; <module-b>/ manifest state → code cross-check required." --> +| Module | Manifest coverage state | What it means here | +|---|---|---| +| `<module>` | <from `.sdd/manifest.json`> | <how strongly the spec can be trusted; whether code cross-check is required> | + +## Autonomy & Ask-First +<!-- Capture: what may proceed without asking vs needs confirmation vs never-without-approval, for THIS repo. + Avoid: blanket autonomy on risky changes. Example: "May: a copy tweak. Ask: a schema change. Never: deploy." --> +- **May proceed:** <low-risk changes that don't touch contracts/data/security> +- **Ask first / plan + confirm:** <contract changes, migrations, security surfaces, anything irreversible> +- **Never without explicit human approval:** <push, deploy, delete data, post to trackers/PRs> + +## Naming +<!-- Capture: the real conventions for files/types/functions/events, with one example. Avoid: a generic style + guide. Example: "Events are past-tense PascalCase: `<ResourceCreated>`, not `<createResource>`." --> +- <real naming conventions, from the codebase, with an example> + +## Logging +<!-- Capture: levels, structured format, correlation/request id, what must NEVER be logged. Avoid: logging PII/ + secrets. Example: "JSON logs; include request_id; never log card numbers or tokens." --> +- <levels, structured format, correlation id, never-log list (see SECURITY.md)> + +## Error Handling +<!-- Capture: the repo's error idiom (exceptions/result types), wrapping/enrichment, user vs internal errors. + Avoid: swallowing errors or leaking internals to users. Example: "Domain errors are typed Result; map to + HTTP at the edge; never leak stack traces." --> +- <error idiom + wrapping + user-facing vs internal> + +## Imports / Dependencies +<!-- Capture: import ordering/boundaries, allowed layering, how new deps are vetted. Avoid: cross-layer imports + or unvetted deps. Example: "domain/ must not import api/; new deps need a lead's approval." --> +- <import ordering/boundaries, allowed layering, dep-vetting> + +## Testing +<!-- Capture: what each change must add (unit + the fitting higher tier), the positive-AND-negative rule, the + coverage bar, where tests live. Avoid: only-happy-path tests. Example: "Each behavior gets a passing and a + must-not-fire test; changed-line coverage ≥ 80%." --> +- <required tests + positive/negative rule + coverage bar + test location> + +## Security +<!-- Capture: the repo-specific security must-dos (defer to SECURITY.md for the full posture). Avoid: restating + all of SECURITY.md. Example: "Validate all input at the edge; authz on every handler; no secrets in code." --> +- <repo-specific must-dos; pointer to SECURITY.md> + +## Spec-Currency & Drift Thresholds +<!-- Capture: the same-change spec rule + the drift thresholds per manifest coverage state. Avoid: merging code without updating + the spec. Example: "manifest state A ≤5% drift; manifest state B ≤15%; manifest state C ≤25%." --> +- Update the spec/docs in the SAME change as the code (spec-currency). +- Drift thresholds: mirror `.sdd/coverage-policy.defaults.yaml` or the repo's stricter manifest policy. + +## Secrets Policy +<!-- Capture: where secrets come from + the never-log/never-commit rule. Avoid: any hardcoded secret. Example: + "Secrets from the manager at boot; CI secret-scans; build fails on a detected secret." --> +- No hardcoded secrets/tokens/keys/connection strings — ever. Source from <secret store>; never log them. + +<!-- Include if: the repo is concurrent/async/reactive --> +## Concurrency & Async +<!-- Capture: what must be non-blocking, ordering guarantees, idempotency expectations. Avoid: blocking the + event loop / assuming single delivery. Example: "Handlers idempotent (keyed by event id); no blocking I/O on the loop." --> +- <threading/reactive rules, ordering, idempotency> + +<!-- Include if: the repo runs automated ticket-to-change or strict-compliance processes --> +## Strict-Compliance Mode +<!-- Capture: when blocking gates apply, retry caps, stop-on-violation. Avoid: silent retries past the cap. + Example: "In auto-runs: stop on first MUST violation; max 3 retries; then escalate." --> +- <when blocking gates apply, retry caps, stop-on-violation behavior> + +## Maintenance +- Add a rule when a review correction recurs; remove it when a lint rule starts enforcing it. +- Cross-reference: patterns → `patterns/`; per-language → `rules/<language>/`. diff --git a/.sdd/templates/component-repo/standing-docs/SECURITY.template.md b/.sdd/templates/component-repo/standing-docs/SECURITY.template.md new file mode 100644 index 000000000..fd08782f6 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/SECURITY.template.md @@ -0,0 +1,84 @@ +<!-- ─────────────────────────────── + Template: Security Baseline + Template-ID: security + Generates: ai-docs/SECURITY.md + Description: Standing security posture — trust boundaries, authn/authz, secret handling, data classification. + Library ver: 0.2.2 + Last updated: 2026-07-11 +─────────────────────────────── --> + +# Security Baseline — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Then this doc; module-specific security behavior lives in each owning module spec. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING reference doc — the security posture an agent must respect on EVERY change (distinct from a + change-specific threat review). Document what the repo ACTUALLY enforces today (file path), not aspirations; + mark gaps `[NEEDS HUMAN INPUT]`. Headings are flat; sections preceded by `<!-- Include if: ... -->` are + kept only when the condition holds. Each section comment gives Capture / Avoid / Example. +--> + +> Read before changing anything that touches input, identity, data, or external calls. Don't weaken a +> documented control without an explicit, approved decision (record it as an ADR). + +## Trust Boundaries +<!-- Capture: each point where untrusted input crosses in, and what's enforced at the crossing. Avoid: assuming + internal == trusted. Example: "Public API edge | internet caller | service | JWT verify + schema validate." --> +| Boundary | Untrusted side | Trusted side | What is enforced at the crossing | +|---|---|---|---| +| <e.g. public API edge> | <caller> | <service> | <authn, input validation, rate limit> | + +## Authentication & Authorization Model +<!-- Capture: how identity is established and how access decisions are made, each with where enforced (file path). + Avoid: per-endpoint ad-hoc checks with no central model. Example: "JWT verified in middleware/auth.ts; + RBAC checked in each handler via requireRole()." --> +- **Authentication:** <mechanism + where verified> (`<file path>`) +- **Authorization:** <model (RBAC/ABAC/ownership) + where enforced> (`<file path>`) +- **Default posture:** <deny-by-default? where the default lives> + +## Secret & Credential Handling +<!-- Capture: where secrets come from, how they're injected, rotation policy. Avoid: secrets in code/env files/ + logs. Example: "From the secret manager at boot; never in source; rotated 90d." --> +- Secrets source: <vault / KMS / secret store — never source code> +- Injection: <how the running code obtains them> +- Rotation: <policy, or `[NEEDS HUMAN INPUT]`> +- **Hard rule:** never commit secrets, tokens, keys, or connection strings; never log them. + +## Data Classification & Handling +<!-- Capture: the data classes handled + the storage/logging/transit rule per class. Avoid: logging PII or + storing it unencrypted. Example: "PII (email) | encrypted at rest | never logged | TLS in transit." --> +| Data class | Examples | Storage rule | Logging rule | In transit | +|---|---|---|---|---| +| <e.g. PII> | <fields> | <encrypted at rest?> | <never log / masked> | <TLS> | + +## Input Validation & Output Encoding Posture +<!-- Capture: the repo-wide expectation for untrusted input and rendered/serialized output. Avoid: trusting + client input or string-concatenating queries. Example: "Validate at the boundary (allow-list); parameterize + all SQL; encode output for its sink." --> +- Validate at the boundary (allow-list where possible); parameterize queries/commands; encode output for its sink. + +<!-- Include if: the repo exposes a network/HTTP API or web surface --> +## Transport & Headers +<!-- Capture: TLS, security headers, CORS/CSRF posture (file path). Avoid: permissive CORS (*) on authed routes. + Example: "HTTPS only + HSTS; CORS allow-list in config/cors.ts; CSRF tokens on state-changing routes." --> +- HTTPS/TLS everywhere; relevant security headers; CORS/CSRF posture (`<file path>`). + +<!-- Include if: the repo handles sessions or cookies --> +## Session & Cookie Posture +<!-- Capture: session id generation, cookie flags, timeout/rotation. Avoid: tokens in localStorage; missing + HttpOnly. Example: "CSPRNG id; Secure+HttpOnly+SameSite=Strict; rotate on login; 30m idle timeout." --> +- Session id generation, cookie flags (Secure/HttpOnly/SameSite), timeout/rotation policy. + +<!-- Include if: the repo has known security-sensitive areas or accepted risks --> +## Known Sensitive Areas & Accepted Risks +<!-- Capture: each sensitive area, its risk, the mitigation/why-accepted, the owner. Avoid: an undocumented + accepted risk. Example: "Legacy import endpoint | no rate limit | behind VPN only | @platform." --> +| Area | Risk | Mitigation / why accepted | Owner | +|---|---|---|---| + +## Reporting & Review +<!-- Capture: who reviews security changes + where to report vulnerabilities. Avoid: no defined path. --> +- Security-relevant changes require <review path>. Suspected vulnerabilities: <where to report>. +- Cross-reference: module-specific security behavior lives in the owning module spec and native + threat-model or security-review source when one exists. diff --git a/.sdd/templates/component-repo/standing-docs/SERVICE_STATE.template.md b/.sdd/templates/component-repo/standing-docs/SERVICE_STATE.template.md new file mode 100644 index 000000000..e7ab2c058 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/SERVICE_STATE.template.md @@ -0,0 +1,82 @@ +<!-- ─────────────────────────────── + Template: Service State (living) + Template-ID: service-state + Generates: ai-docs/SERVICE_STATE.md + Description: Living as-built registry — current endpoints/events/stores/deps/limits/metrics/flags; read first to avoid duplicates. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Service State (living) — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). Read this FIRST before adding a surface; stable contracts in `CONTRACTS.md`. +> Context-efficiency: link to canonical docs — don't duplicate them; load on demand, not upfront. + +<!-- + STANDING, LIVING current-state registry — the as-built snapshot of what this service exposes and depends on + RIGHT NOW. Read FIRST before adding an endpoint/event/dependency, to avoid duplicating or breaking it. + Describes reality, not intent. Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept + only when the condition holds. Each section comment gives Capture / Avoid / Example. Fill from real + code/config (file path) at the current SHA. +--> + +> Source of truth for "does X already exist?" Keep current in the same change that adds/removes a surface. + +<!-- Include if: the service exposes endpoints --> +## Current Endpoints +<!-- Capture: every endpoint that exists now — method+path, handler, auth/scope, gating flag. Avoid: listing a + planned endpoint. Example: "GET /<resource> | <ResourceController>.list | read:<resource> | (no flag)." --> +| Method + path | Handler / controller | Auth / scope | Feature flag (if any) | +|---|---|---|---| + +<!-- Include if: the service publishes or consumes events --> +## Current Events +<!-- Capture: each event in/out now + producer/consumer + payload ref. Avoid: omitting direction. Example: + "<ResourceCreated> | publish | <module> | events/<resource>.ts." --> +| Event / topic | Direction | Producer/consumer | Payload ref | +|---|---|---|---| + +<!-- Include if: the service owns or uses data stores --> +## Data Stores +<!-- Capture: each store, its purpose, and whether THIS service owns it. Avoid: claiming ownership of a shared + store. Example: "<datastore> `<resource_table>` | <resource> records | owned: yes." --> +| Store | Purpose | Owned by this service? | +|---|---|---| + +## External Dependencies +<!-- Capture: each outbound dependency + resilience (timeout/retry, breaker/fallback). Avoid: a dependency with + no timeout. Example: "<external-service> | <lookup purpose> | 2s timeout, 3 retries | breaker → last-good cache." --> +| Dependency | Used for | Timeout / retry | Circuit breaker / fallback | +|---|---|---|---| + +<!-- Include if: the service enforces rate limits / quotas --> +## Rate Limits & Quotas +<!-- Capture: each limited surface + the limit + its scope. Avoid: an undocumented limit that surprises callers. + Example: "POST /<resource> | 100/min | per API key." --> +| Surface | Limit | Scope (per user/tenant/global) | +|---|---|---| + +<!-- Include if: the service has defined SLOs / performance targets --> +## Key Metrics & Performance Targets +<!-- Capture: the signals that matter + their target + where measured. Avoid: vanity metrics with no target. + Example: "p99 latency | < 200ms | dashboard X." --> +| Signal | Target | Where measured | +|---|---|---| + +## Feature Flags (current) +<!-- Capture: each live flag — what it gates, current default, owner, removal condition. Avoid: re-adding or + mis-defaulting an existing flag. Example: "<newCapability> | new capability path | OFF | <owner> | remove after GA." --> +| Flag | Gates | Current default | Owner | Safe to remove when | +|---|---|---|---|---| + +<!-- Include if: the service holds compliance certifications/obligations worth surfacing --> +## Compliance / Certifications +<!-- Capture: the obligations an agent must not regress. Avoid: omitting a regulated constraint. Example: + "PCI scope: card data never logged or stored in plaintext." --> +- <relevant obligations an agent must not regress> + +## Maintenance +<!-- Capture: update the relevant row in the SAME change that alters a surface/dep/limit/flag. Avoid: letting + the registry drift from reality. --> +- Update the relevant row in the same change that adds/changes/removes a surface, dependency, limit, or flag. +- Cross-reference: stable contracts → `CONTRACTS.md`; entities → `DATA_MODEL.md`; security posture → `SECURITY.md`. diff --git a/.sdd/templates/component-repo/standing-docs/SPEC_INDEX.template.md b/.sdd/templates/component-repo/standing-docs/SPEC_INDEX.template.md new file mode 100644 index 000000000..3fc6109b9 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/SPEC_INDEX.template.md @@ -0,0 +1,94 @@ +<!-- ─────────────────────────────── + Template: Spec Index + Template-ID: spec-index + Generates: ai-docs/SPEC_INDEX.md + Description: Router — which docs to load for which task and the canonical module registry. + Library ver: 0.2.2 + Last updated: 2026-07-31 +─────────────────────────────── --> + +# Spec Index — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry). This file is the router (generated at `ai-docs/SPEC_INDEX.md`); system overview in [`ARCHITECTURE.md`](ARCHITECTURE.md). Load `AGENTS.md` + this file first; pull every other doc on demand. +> Context-efficiency: link to canonical docs — don't duplicate them; route to the minimum needed per task. + +<!-- + ROOT FILE 3 of 3. The ROUTER — tells an agent which docs to load for which task; holds the module + registry and incident-history reference rows. Human-readable mirror of + .sdd/manifest.json. Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when + the condition holds. Each section comment gives Capture / Avoid / Example. +--> + +> AI agent entry point after `AGENTS.md`. Load this once at session start; pull other docs on demand. +> **Source of truth:** `.sdd/manifest.json` (this file mirrors it for humans). + +## Module Registry +<!-- Capture: one row per module mirroring the manifest — responsibility, manifest coverage state, and + the module spec link. Module-specific details live in the single module spec, so this router stays + compact. Avoid: drifting from .sdd/manifest.json (it is authoritative). + + NESTING: modules form a tree. Show depth with one `↳ ` prefix per level BEFORE the backticked path, + never inside it, and keep rows in depth-first order so each child follows its parent. Depth must + increase by at most one per row — a jump means a parent row is missing. Keep the table at four + columns; depth lives in the Module cell, not a fifth column. A top-level module carries no prefix. + + A child's path always BEGINS WITH its parent's path, but the two need not be adjacent: source-layout + directories that are not themselves modules (`src/`, `main/`, `java/`, `res/`…) sit in between and are + written out in full. They do not add depth — one `↳ ` per MODULE level, never one per path segment. So + a module at `billing/src/main/java/ledger/` whose nearest module ancestor is `billing/` is depth 1 and + takes a single marker. + Example: + "`<module>/` | <responsibility> | manifest state | `<module-path>/ai-docs/<module-name>-spec.md` + ↳ `<module>/src/main/<child>/` | <what the child owns> | manifest state | `<module>/src/main/<child>/ai-docs/<child>-spec.md`" --> +| Module | Responsibility | Manifest coverage state | Start here | +|---|---|---|---| +| `<module>/` | <one line> | <from `.sdd/manifest.json`> | `<module-path>/ai-docs/<module-name>-spec.md` | +| ↳ `<module>/.../<child>/` | <one line> | <from `.sdd/manifest.json`> | `<module>/.../<child>/ai-docs/<child>-spec.md` | + +## Task Routing +<!-- Capture: for each kind of work, exactly which docs to load (keep token usage low). Avoid: "load + everything". Example: "Working in a module → that module's spec; load the relevant section only." --> +| If the task is… | Load | +|---|---| +| Understanding the system | `ARCHITECTURE.md` | +| Working in `<module>` | `<module-path>/ai-docs/<module-name>-spec.md` | +| A cross-service contract change | the relevant contract docs + `ARCHITECTURE.md` interaction section | +| Running or changing tests | `TEST_INDEX.md` + the affected module spec or feature test strategy | +| Updating docs after a code change | affected module specs + relevant standing indexes/contracts | +| Migrating existing specs | manifest source routes + affected module specs + source-fidelity report | + +## Incident History +<!-- Capture: a one-line REFERENCE row per incident with a link to the full RCA (ticket/wiki). Avoid: pasting + full RCAs here. Example: "INC-3014 | 2026-05-02 | <module>/ | <failure mode> | <link>". --> +| INC id | Date | Module | One-line | Link | +|---|---|---|---|---| +| <INC-xxxx> | <YYYY-MM-DD> | `<module>/` | <what happened> | <url> | + +<!-- Include if: the repo is large enough that phase-based on-demand spec loading is worthwhile --> +## Phase-Based Loading Protocol +<!-- Capture: which docs to load per phase to bound token usage. Avoid: front-loading every spec. Example: + "Orient → AGENTS + this file; Build → the one module spec + its rules." --> +| Phase | Load | +|---|---| +| Orient | AGENTS.md + this file | +| Specify | relevant module docs and routed source specs | +| Build | the selected module SPEC(s) + patterns/rules | +| Verify | independent validation | +*Token-budget table and strict-compliance loading go here if the repo runs automated ticket-to-change processes.* + +## Spec Registry +<!-- Capture: where each standing doc lives so an agent can route to it. Avoid: linking docs that don't exist + in this repo — drop rows that don't apply. Example: keep DATA_MODEL only if the repo owns data. --> +| Doc | Location | Purpose | +|---|---|---| +| Patterns | `patterns/` (+ `<lang>/`) | repo conventions, correct vs incorrect | +| Rules | `RULES.md` + `rules/` (+ `<language>/`) | enforceable do/don't beyond AGENTS.md critical rules | +| Glossary | `GLOSSARY.md` | ubiquitous language: term → definition → code location | +| Security | `SECURITY.md` | trust boundaries, authn/authz, secret handling, data classification | +| Contracts | `CONTRACTS.md` | root index of public-surface contracts; details live at owning modules or native contract sources | +| Data model | `DATA_MODEL.md` | entities, ownership, relationships, migration discipline (if the repo owns data) | +| Service state | `SERVICE_STATE.md` | living as-built registry — read first to avoid duplicate/breaking surfaces | +| Test index | `TEST_INDEX.md` | test tiers, canonical commands, locations, frameworks, dependencies, and quality gates | +| Getting started | `GETTING_STARTED.md` | clone/build/run + multi-repo workspace layout | +| Decision records | `adr/` | standing ADRs — why the architecture is the way it is | +| Review catalog | `REVIEW_CHECKLIST.md` | the 6-core + 4-coverage + 3-cross-cutting review checks | diff --git a/.sdd/templates/component-repo/standing-docs/TEST_INDEX.template.md b/.sdd/templates/component-repo/standing-docs/TEST_INDEX.template.md new file mode 100644 index 000000000..491d78254 --- /dev/null +++ b/.sdd/templates/component-repo/standing-docs/TEST_INDEX.template.md @@ -0,0 +1,64 @@ +<!-- ─────────────────────────────── + Template: Test Index + Template-ID: test-index + Generates: ai-docs/TEST_INDEX.md + Description: Repo-wide test surface — tiers, commands (by role), directories, frameworks, and coverage gate — routing to where cases live. + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# Test Index — <repo name> + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). This doc is the repo-wide map of the test surface. +> Context-efficiency: this is an INDEX, not a case list. It links to where cases live — it does not duplicate them. + +<!-- + STANDING reference doc — the one place to understand the whole test surface of the repository: which + test tiers exist, the command role that runs each, where the tests live, the framework, external + dependencies, and the enforced coverage/quality gate. It is mirrored from the machine-readable + `.sdd/manifest.json` (`commands`, `tests`, `quality_gates`) and MUST NOT restate the actual test cases: + - per-module unit-test detail lives in each module spec's "Test-Case Strategy (module)" section; + - per-feature/system test detail lives in the feature test strategy at + `features/<KEY>/test-strategy.md`. + Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition + holds. Each section comment gives Capture / Avoid / Example. Fill every command/dir/framework from the + real build config (file path) — never guess. +--> + +## Test Surface +<!-- Capture: one row per test tier the repo actually runs, mirrored from the manifest `tests` block and + `commands` (by role). Name the command by its role so a reader/workflow runs the right one; give the + directory and framework from the real test setup; list external dependencies the tier needs (e.g. + containers). Avoid: inventing a tier the repo doesn't have, or defaulting to a web/JS framework when + the repo isn't web. Example: "Unit | mvn -pl '!test' test | src/test | JUnit 5 | none". --> +| Tier | Command (role) | Test directory | Framework | External deps | +|---|---|---|---|---| +| Unit | `<unit-test cmd>` | `<dir>` | `<framework>` | <none / list> | +| Integration | `<integration-test cmd>` | `<dir>` | `<framework>` | <e.g. docker: postgres, redis> | +| E2E / System | `<e2e cmd>` | `<dir>` | `<framework>` | <deps> | + +## Where the Cases Live +<!-- Capture: the routing so a reader finds the actual cases without this doc duplicating them. Avoid: + copying test cases here. --> +- **Unit test cases** → each module's spec, "Test-Case Strategy (module)" section (see `SPEC_INDEX.md` for the module registry). +- **Integration / E2E / scale / security / resiliency / QA cases** → the per-feature test strategy at `features/<KEY>/test-strategy.md`. + +<!-- Include if: the repo enforces a coverage / static-analysis / lint gate --> +## Coverage / Quality Gate +<!-- Capture: the enforced gate, mirrored from the manifest `quality_gates.code_coverage`: the minimum, + what it measures (Sonar / lint / unit-test coverage), whether it applies to the whole codebase or + changed lines, and where it is enforced. This gate often lives outside the repo build (Sonar/CI/org + policy); record it as stated by the repo owner, not a guessed default. Avoid: implying a platform + default is a repo rule, or claiming a gate the repo doesn't enforce. Example: "≥ 85% (Sonar), changed + lines, enforced in CI." --> +- Minimum: `<n>%` · Measures: <sonar / lint / unit-test> · Applies to: <whole codebase / changed lines> · Enforced in: <where>. + +## QA Dependencies & Environments +<!-- Capture: standing test-environment or external-team dependencies the repo's testing relies on, and + where manual/QA cases are tracked. Avoid: assuming a test environment is always available. Example: + "staging bulk-data set for load tests; manual QA cases tracked in the QA tracker project." --> +- <standing test env / external dependency / manual-QA tracker location — or "none"> + +## Where to Go Next +- Agent entry: `../AGENTS.md` · System shape: `ARCHITECTURE.md` · Routing: `SPEC_INDEX.md` +- Machine source of truth: `.sdd/manifest.json` (`commands`, `tests`, `quality_gates`). diff --git a/.sdd/templates/feature-package/README.md b/.sdd/templates/feature-package/README.md new file mode 100644 index 000000000..5eb33432e --- /dev/null +++ b/.sdd/templates/feature-package/README.md @@ -0,0 +1,93 @@ +# Feature-Package Templates + +The per-change lifecycle templates — instantiated **per change** (a feature, a defect, or +module-spec work) during Capture → Discovery → Development. Capture includes intake and feature-spec +capture. Discovery includes discovery, design, test strategy, and decomposition. Development includes +implementation planning and code. One stage-agnostic set; stage generators fill the right sections at +the right stage. + +> **Seeded into the target repo.** A repository setup process copies these templates unchanged into the +> target repo's `.sdd/templates/`. Later lifecycle generators instantiate them from the target repo, not +> from a generator-owned bundle. + +## Generated Flow + +When instantiated for a change, these templates produce artifacts in this order: + +```text +Capture + -> intake run record + -> features/<KEY>/spec/feature-spec.md +Discovery + -> features/<KEY>/design/feature-design.md + -> features/<KEY>/design/contracts/*.md when needed + -> features/<KEY>/test-strategy.md + -> features/<KEY>/tasks/<epic>/epic.md + -> features/<KEY>/tasks/<epic>/task-<n>.md +Development + -> features/<KEY>/tasks/<epic>/implementation-plan-<n>.md plus required docs, code, and test changes +Module-spec work + -> <module-path>/ai-docs/<module-name>-spec.md plus SPEC_INDEX.md routing +``` + +Use a generator or manual process that follows this artifact order to fill these templates. Edit this +folder only when changing the reusable shape of future feature artifacts. + +**Altitude split:** the Feature Spec owns product intent (WHAT and WHY). The Feature Design owns the +technical solution. Contracts under `design/contracts/` own full interface schema. Tasks own PR-sized +implementation boundaries. Do not restate the same fact across layers; link to the owner. + +Generated feature artifacts should keep the source-template heading order and fill retained sections +with concrete detail. Use `N/A` only with a reason, and use `[NEEDS HUMAN INPUT]` only while a required +answer is unresolved. For design sequence diagrams, first map operation groups to diagrams, then +include primary and failure/rollback/recovery paths for each operation group that needs a sequence view. + +| Template | Generates | Purpose · when to use (stage) | +|---|---|---| +| `spec/feature-spec.template.md` | `feature-spec.md` | Product intent: WHAT+WHY, scope, acceptance, success/guardrail metrics, requirements state, contracts delta, change log. **Capture.** | +| `design/feature-design.template.md` | `feature-design.md` | The discovery design container — feature architecture (system context, decomposition, object model, alternatives, views, toggle), scale, service-impact, interfaces, rollout, coverage summary, sign-off. **Discovery / design.** | +| `design/contracts/_contract.template.md` | `contracts/<iface>.md` | One cross-service interface: full schema, error catalog, backward-compat, delivery/ordering, versioning. **Discovery / design**, per non-trivial interface. | +| `tasks/epic.template.md` | `epic.md` | A coherent slice of the design → child tasks, sequencing, exit criteria. **Discovery / decomposition.** | +| `tasks/task.template.md` | `task-<n>.md` | A PR-sized task with an explicit **ownership boundary**, acceptance + verifier-exit criteria, traceability, coverage. **Discovery / decomposition.** | +| `implementation/implementation-plan.template.md` | `implementation-plan-<n>.md` | Per-task dev plan: current context, approach, changes, rollback, anticipated PR split. **Development.** | +| `test-strategy/test-strategy.template.md` | `test-strategy.md` | Feature/system test plan (unit tests live in the module spec): use-cases→tests + contract/integration/E2E/scale/security/resiliency tiers. **Discovery.** | +| `intake/feature-intake.template.md` | `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` | Code-grounded intake questions for a new feature; the generated file is a run record, not a canonical spec. **Capture / intake.** | +| `intake/bug-intake.template.md` | `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` | Code-grounded intake questions for a defect or behavior correction; the generated file is a run record. **Capture / intake.** | +| `intake/module-spec-intake.template.md` | `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` | Code-grounded intake questions for a new module or deeper module spec; the generated file is a run record that feeds `<module-path>/ai-docs/<module-name>-spec.md`. **Capture / intake.** | + +## Per-feature instance layout + +``` +<repo>/features/<KEY>/ (or docs/specs/<KEY>/ — repo's choice, set in SPEC_INDEX) + spec/feature-spec.md + design/feature-design.md (discovery design container) + design/contracts/*.md (one per cross-service interface, when they exist) + tasks/<epic>/epic.md (decomposition: a coherent slice of the design) + tasks/<epic>/task-<n>.md (decomposition: a PR-sized task) + tasks/<epic>/implementation-plan-<n>.md (implementation: per-task dev plan) + test-strategy.md + +<repo>/.generated/sdd/features/<KEY>/ + lifecycle-state.json (machine-readable stage gates and source readiness) + run-records/ (stage Q&A/provenance; not canonical specs) + intake-questionnaire.md + capture-questionnaire.md + discovery-questionnaire.md + decomposition-decisions.md + implementation-record.md +``` + +> The standard generated layout is `features/<KEY>/...`; template navigation links assume this layout. +> If a repository chooses another location, the generator must rewrite relative links during +> instantiation and record the location in `SPEC_INDEX.md`. + +Current feature packages use stage-specific records under +`.generated/sdd/features/<KEY>/run-records/` plus `.generated/sdd/features/<KEY>/lifecycle-state.json` +so agents can load the minimum stage context without confusing provenance with canonical specs. Older +packages may still have `features/<KEY>/questionnaire.md` as a compatibility fallback. + +The lifecycle chain: **Capture** (intake + Feature Spec) → **Discovery** (Feature Design + Test +Strategy + Epic/Task decomposition) → **Development** (Implementation Plan → code). + +Conventions (metadata header · navigation pointer · context-efficiency · flat headings + `Include if:` +· Capture/Avoid/Example) are described in `../README.md`. diff --git a/.sdd/templates/feature-package/design/contracts/_contract.template.md b/.sdd/templates/feature-package/design/contracts/_contract.template.md new file mode 100644 index 000000000..bbc3b5990 --- /dev/null +++ b/.sdd/templates/feature-package/design/contracts/_contract.template.md @@ -0,0 +1,83 @@ +<!-- ─────────────────────────────── + Template: Contract (per-interface) + Template-ID: contract + Generates: features/<KEY>/design/contracts/<interface>.md + Description: One cross-service interface — definition, error catalog, backward-compat, delivery/ordering, versioning. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Contract — <interface name> + +> Start here → repo root [`AGENTS.md`](../../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md). This contract belongs to the design [`feature-design.md`](../feature-design.md); the standing catalog is [`CONTRACTS.md`](../../../../ai-docs/CONTRACTS.md). (Links relative to this file.) +> Context-efficiency: link to canonical docs — don't duplicate them. Link the canonical schema/API source when one exists; only inline a compact schema when no machine-readable source exists yet. + +<!-- + Per-interface contract — one file per cross-service interface a feature adds or changes. Holds the schema + pointer or compact schema, error catalog, and backward-compat so producer and consumers agree exactly. Save under + features/<KEY>/design/contracts/<interface>.md; reference it from the Feature Design interface table. + Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition holds. + Each section comment gives Capture / Avoid / Example. Fill from real code (file path). +--> + +<!-- Ownership: this file owns the full schema/error/version contract for one interface changed by the + feature. Feature Spec owns the product-level delta; CONTRACTS.md owns the stable as-built catalog. --> + +## Metadata +<!-- Capture: the interface identity — kind, change type, producer, consumers. Avoid: omitting consumers (you + can't assess breakage). Example: "Kind: network API; Change: new; Producer: <producer>; Consumers: <consumer>." --> +| Field | Value | +|---|---| +| Interface | <name> | +| Kind | network API / event / schema / RPC | +| Change type | new / modify / remove | +| Producer | `<service/module>` | +| Consumer(s) | `<service/module(s)>` | +| Canonical schema / API source | <OpenAPI/AsyncAPI `.yaml`, `.proto`, `.graphql`, JSON Schema, SDK API report, or none yet> | +| Feature | `../feature-design.md` | +| Generated from | `contract` @ SDLC template library `0.2.2` | + +## Summary +<!-- Capture: one or two sentences — what the interface is for and what's changing. Avoid: a restatement of the + name. Example: "<Operation> endpoint; new in this feature." --> +<summary> + +## Definition +<!-- Capture: the exact surface by linking the canonical schema/API source. Inline only a compact schema when + no machine-readable source exists yet. Avoid: prose where a schema is needed or duplicating a full + OpenAPI/AsyncAPI/proto/SDK contract. Example: "openapi.yaml#/paths/~1resource/post" or a compact JSON + request {ids: string[] (required)} -> {results: [...]}. --> +- **Canonical source:** <schema/API link, or "none yet"> +- **Inline definition if no canonical source exists:** <compact request/response or message schema> + +## Error / Failure Catalog +<!-- Capture: every error the interface can return/raise + the consumer's action. Avoid: an undocumented failure + mode. Example: "PartialFailure (207) | some items not processed | inspect per-item status." --> +| Condition | Code / signal | Meaning | Consumer action | +|---|---|---|---| + +## Backward Compatibility +<!-- Capture: whether compatible + the consumer transition path if not. Avoid: a breaking change with no transition plan. + Example: "Compatible — additive; no consumer change needed." --> +- **Compatible?** <yes/no> — <reasoning> +- **Consumer transition / deprecation:** <coexistence plan; deprecation window; version bump> + +<!-- Include if: the interface has ordering, delivery, or idempotency guarantees (events/messaging) --> +## Delivery & Ordering Guarantees +<!-- Capture: at-least-once/exactly-once, ordering, idempotency key, replay behavior. Avoid: leaving delivery + semantics implicit. Example: "at-least-once; idempotency key = itemId+batchId; safe to replay." --> +- <delivery / ordering / idempotency> + +<!-- Include if: the interface is versioned --> +## Versioning +<!-- Capture: the version scheme + how producer/consumers negotiate it. Avoid: changing a version with no + negotiation path. Example: "URI version /v2; v1 supported for one release." --> +- <version scheme; negotiation> + +## Validation +<!-- Capture: how conformance is checked (contract test / schema validation / consumer-driven contract) + where + it runs. Avoid: shipping a contract with no test. Example: "consumer-driven contract test in CI." --> +- <test/scheme + where it runs in CI> + +## References +- Feature design: `../feature-design.md` · Standing catalog: [`CONTRACTS.md`](../../../../ai-docs/CONTRACTS.md) · Owning module spec: <module spec path> · Baseline: `.sdd/manifest.json` diff --git a/.sdd/templates/feature-package/design/feature-design.template.md b/.sdd/templates/feature-package/design/feature-design.template.md new file mode 100644 index 000000000..a6a90b4bc --- /dev/null +++ b/.sdd/templates/feature-package/design/feature-design.template.md @@ -0,0 +1,304 @@ +<!-- ─────────────────────────────── + Template: Feature Design + Template-ID: feature-design + Generates: features/<KEY>/design/feature-design.md + Description: Discovery design container — feature architecture, contracts, scale, rollout, coverage summary, sign-off. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Feature Design — <feature title> + +> Start here → repo root [`AGENTS.md`](../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). This design serves the spec [`feature-spec.md`](../spec/feature-spec.md); it feeds the decomposition [`../tasks/`](../tasks/) and test plan [`test-strategy.md`](../test-strategy.md). (Links relative to this file.) +> Context-efficiency: link to canonical docs — don't duplicate them; reference the repo ARCHITECTURE/CONTRACTS rather than restating them. +> Keep lean: this document owns the technical solution for this feature; link to standing docs and +> per-interface contracts instead of copying their full content. + +<!-- + The DISCOVERY design-document container — the home for a feature's architecture and the plan to build it. + Its **Feature Architecture** section is the core. Save as features/<KEY>/design/feature-design.md. Headings + are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition holds. Fill every + <...> from REAL code/evidence (file path). Where a section doesn't apply, write the heading + "N/A — + <reason>" so the Design Coverage Summary shows nothing was skipped. Each section comment gives Capture / + Avoid / Example. +--> + +## Metadata +<!-- Capture: identity + link to the spec it serves + status + change class. Avoid: a design with no link to the + WHAT/WHY it implements. Example: "Status: soft-committed; Change class: contract-affecting." --> +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Title | <title> | +| Feature Spec | `../spec/feature-spec.md` (WHAT + WHY this design serves) | +| Status | discovery / soft-committed / tasked / implementation-ready | +| Change class | <routine / security / contract-affecting / perf-critical / persistence / ui> | +| created_by / approved_by / date | <provenance> | +| Generated from | `feature-design` @ SDLC template library `0.2.2` | + +## Executive Summary +<!-- Capture: one paragraph — what's being built and the shape of the approach, linking back to requirements. + Avoid: diving into detail before orientation. Example: "Adds a `<bulk operation>` path in `<module>` + behind a flag, reusing the existing single-item logic per item (R-1, R-2)." --> +<one paragraph> + +## Scenario → Design Map +<!-- Capture: each requirement/scenario → the design element that satisfies it (forward traceability). Avoid: + a requirement with no design element. Example: "R-1 `<bulk operation>` → `<OperationBlock>` + POST /<resource>:<operation>." --> +| Requirement / scenario (from Feature Spec) | Design element that satisfies it | +|---|---| +| <scenario> | <block / interface / object / flow> | + +--- + +# Feature Architecture +<!-- + THE CORE OF THIS DOCUMENT. The architecture of THIS feature across the services/modules it touches — + distinct from the repo-wide architecture doc and a module's manifest-routed spec. Ground every claim in real + code (file path) at the current SHA. +--> + +## System Context +<!-- Capture: the feature's place in the wider system — external entities/services it talks to + the boundary + of this change (a context diagram helps). Avoid: redrawing the whole system. Example: "<module> + + <caller> and <downstream bus>; <adjacent system> is out of scope." --> +``` +<context diagram: this feature + external systems/actors it interacts with> +``` +<narrative: what is inside vs outside the boundary of this change> + +## Functional-Block Decomposition +<!-- Capture: the feature's logical blocks + how they interact (the actual feature architecture); one block per + coherent responsibility. Avoid: a single "does it all" block. Example: "<Request> validator → <Operation> + orchestrator → per-item action → event emitter." --> +``` +<block diagram for this feature: blocks + the calls/events/data between them> +``` +| Block | Responsibility (for this feature) | New or existing | Touches module(s) | +|---|---|---|---| +| <block> | <what it does> | new / existing | `<module>` | + +## Object-Model Changes +<!-- Capture: the new/changed domain objects the implementer will touch. Avoid: omitting a field change that + breaks consumers. Example: "<Entity>: changed — add `<field>` (nullable)." --> +| Object / entity | New / changed / removed | Fields / shape change | Owning module | +|---|---|---|---| + +## Design Decisions & Rationale +<!-- Capture: each decision + WHY, at feature altitude. Avoid: a decision with no rationale. Example: "D-1 reuse + single-item behavior in a loop — why: keeps one code path; bulk behavior is orchestration." --> +- **D-1 <decision>:** <what> — **why:** <rationale> + +## Alternatives Explored +<!-- Capture: options considered, pros/cons, why dismissed (stops re-litigation). Avoid: presenting one option + as if no others existed. Example: "A direct storage write path — faster, but bypasses domain validation — rejected." --> +| Alternative | Pros | Cons | Why not chosen | +|---|---|---|---| + +## Dependencies & Assumptions +<!-- Capture: what must hold for the design to work + upstream deps (traced to tickets). Avoid: an unstated + assumption that silently breaks the design. Example: "Assumes the event bus is at-least-once; depends on + `<service>` v2 being deployed." --> +- **Assumes:** <assumption — and what breaks if false> +- **Depends on:** <upstream feature / service / migration — link> + +<!-- Include if: one diagram does not capture the design — add the views that matter (logical / security / deployment / data-flow). [condition-id: feature.needs_arch_views] --> +## Architecture Views +<!-- Capture: only the views that add information beyond the block diagram. Avoid: redundant views that repeat + the same picture. Example: a deployment view showing the new worker pool. --> +- **Logical view:** <diagram/narrative> +- **Deployment view:** <diagram/narrative> +- **Data-flow view:** <diagram/narrative> + +--- + +## Feature-Toggle Strategy +<!-- Capture: the toggle(s) gating the feature — name, OFF behavior, safe default, owner, removal trigger. Avoid: + a flag defaulting ON, or no removal plan. Example: "<operationFlag> | gates the endpoint | documented OFF response | OFF | + <owner> | remove after GA." --> +| Toggle | Gates | Behavior when OFF | Default | Owner | Removal trigger | +|---|---|---|---|---|---| + +<!-- Include if: the feature has throughput/latency/concurrency expectations (perf-critical or high-volume). [condition-id: feature.scale_requirements] --> +## Scale Requirements +<!-- Ownership: technical capacity and scale design. Product-level target/guardrail metrics live in + feature-spec.md. --> +<!-- Capture: the concrete throughput/concurrency/latency/volume targets. Avoid: numbers copied from another + feature. Example: "Throughput: <n> batches/s; Latency p99: < <bound>/batch of <size>." --> +| Dimension | Requirement | +|---|---| +| Throughput (req/s or events/s) | <value> | +| Concurrency | <value> | +| Latency p80 / p90 / p99 | <values> | +| Data volume / growth | <value> | + +## Impacted Services / Modules & Task Split +<!-- + Capture: one block per impacted service group; keep the split high-level. The authoritative PR-sized + breakdown is formalized in ../tasks/ (epic + task + implementation-plan). Avoid: maintaining the same + task list in two places. + Example: "<module-a> — endpoint/API changes; <worker> — async processing; <client> — user action." +--> +For each impacted service group / module: + +### <Service group or module name> +- **Deployment target:** <where it runs / ships> +- **Epic:** <delivery epic key/URL, or "TBD until tasked"> +- **Changes:** + - <high-level change area; detailed tasks live in ../tasks/> + +<!-- Include if: the feature introduces a brand-new service/component. [condition-id: feature.new_service] --> +### New Services +<!-- Capture: each new service + purpose + deployment target + cost + why new vs extend. Avoid: a new service + where extending an existing one would do. Example: "<operation-worker> | async batch processing | <platform> | extend + rejected: isolation needed." --> +| Service | Purpose | Deployment target | Cost profile | Justification (why new vs extend) | +|---|---|---|---|---| + +## Service Impact Matrix +<!-- Capture: who changes AND who explicitly does NOT (blast radius). Avoid: omitting the explicit "no"s. + Example: "<module-b> | no | unaffected — `<operation>` does not touch `<domain area>`." --> +| Service / module | Changes? | What changes (or why not) | Owner | +|---|---|---|---| +| `<service>` | yes / **no** | <one line> | <team> | + +<!-- Include if: the feature changes datastores, messaging, or storage infrastructure. [condition-id: feature.infra_changes] --> +## Platform / Infrastructure Changes +<!-- Capture: the DB/messaging/storage/cloud change + migration plan. Avoid: a schema change with no migration. + Example: "new <broker> topic <domain.operation>; add the consumer group." --> +- <DB / messaging / storage / cloud-service change + migration/true-up plan> + +<!-- Include if: the feature requires CI/CD pipeline changes. [condition-id: feature.cicd_changes] --> +## CI/CD Pipeline Changes +<!-- Capture: new build/test/deploy steps, gates, environments. Avoid: a new component with no pipeline. Example: + "add a load-test stage gated before prod for the high-volume path." --> +- <new build/test/deploy steps, gates, environments> + +## Interface & Contract Definitions +<!-- Ownership: interface inventory and links. Full schema/error/version details live in + design/contracts/*.md and the native schema/API source when one exists; stable as-built surfaces are + summarized in the owning module spec, indexed from ai-docs/CONTRACTS.md, and reflected in SERVICE_STATE.md. --> +<!-- Capture: one row per interface added/changed (producer/consumer/change-type) + links to its contract doc + and schema/API source. Avoid: inventing a route shape here or pasting a full schema. Example: + "POST /<resource>:<operation> | <producer> | <consumer> | new | contracts/<operation>.md | + openapi.yaml#/paths/... | additive." --> +| Interface (API / event / schema) | Producer | Consumer(s) | Change type (new/modify/remove) | Contract doc | Schema / API source | Compatibility / deprecation | +|---|---|---|---|---|---|---| +| <interface> | <svc> | <svc> | new / modify / remove | `contracts/<name>.md` | `<schema-or-api-detail>` | <compatible / transition plan> | + +<!-- Include if: this feature adds/changes a non-trivial interface — write one per-interface contract doc under contracts/. [condition-id: feature.nontrivial_interface] --> +> Per-interface contract documents live in `contracts/*.md` (i.e. `design/contracts/`), one per +> cross-service interface. Prefer native schema files (`.yaml` for OpenAPI/AsyncAPI unless the repo +> already uses `.yml`, `.proto`, `.graphql`, JSON Schema, or SDK API reports) as the exact source. + +<!-- Include if: the feature changes the data model (schema, migration, caching). [condition-id: feature.data_model_changes] --> +## Data-Model Design +<!-- Ownership: technical schema/migration/cache design for this feature. Standing entity ownership lives + in DATA_MODEL.md. --> +<!-- Capture: schema changes + migration/true-up + cache effects. Avoid: a destructive migration. Example: "add + nullable column expand→migrate→contract; no backfill." --> +- <schema changes; migration / true-up plan; cache patterns + TTL/invalidation> + +<!-- Include if: the feature has authz/RBAC or data-privacy implications. [condition-id: feature.security_rbac] --> +## Security / RBAC Design +<!-- Ownership: technical authz/RBAC/privacy design for this feature. Standing security posture lives in + SECURITY.md. --> +<!-- Capture: scopes, authz model, data classification/privacy, trust-boundary changes. Avoid: a privileged op + with no scope check. Example: "requires privileged scope checked per target boundary." --> +- <scopes, authz model, data classification/privacy, trust-boundary changes> + +<!-- Include if: the feature crosses a wire protocol / binary format (e.g. gRPC, a SOAP/XML protocol). [condition-id: feature.wire_protocol] --> +## Protocol / Wire-Format Design +<!-- Capture: the protocol-level design. Avoid: inventing a wire format — ground it in the owning repo. Example: + "extends the existing `<ProtocolService>` with a `<BatchOperation>` RPC." --> +- <protocol-level design and canonical protocol/schema source> + +<!-- Include if: the feature has a user-visible surface. [condition-id: feature.user_visible_surface] --> +## UX Design & Traceability +<!-- Capture: the user flow + Figma link + each UX element → its backing API. Avoid: a UI with no backing API + mapping. Example: "<bulk action control> → POST /<resource>:<operation>." --> +- User-visible flow (screens/states); **Figma:** <link>. +| UX element | Backing API / interface | +|---|---| + +<!-- Include if: the feature has bulk / CSV / batch behavior. [condition-id: feature.bulk_batch] --> +## Bulk / CSV / Batch Design +<!-- Capture: the bulk contract, column spec, batch sizing, partial-failure semantics. Avoid: an all-or-nothing + batch where partial success matters. Example: "max <limit>/batch; return per-row status; no whole-batch rollback." --> +- <bulk contract, column spec, batch sizing, partial-failure semantics> + +## HA & Failure-Condition Matrix +<!-- Capture: failure conditions × probability × impact × mitigation (the resiliency posture). Avoid: assuming + dependencies never fail. Example: "bus unavailable | med | events lost | buffer + retry on recovery." --> +| Failure condition | Probability | Impact | Mitigation / fallback | +|---|---|---|---| + +<!-- Include if: the primary or error/rollback paths warrant a sequence view. [condition-id: feature.needs_sequence_diagrams] --> +## Sequence Diagrams +<!-- Capture: the sequence inventory first, then one sequence per major operation group. Count operation + groups from scenarios, public/API surfaces, events, commands, async jobs, cross-repo rollout steps, + and state transitions. Merge operations into one diagram only when they share the same actors, + ordering, transport, state transition, and failure behavior. Include error/rollback/retry/recovery + paths. Avoid: one generic happy-path diagram for a design with multiple behaviors. Example: a mermaid + sequence of the batch with a per-user failure branch. --> +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| `<operation group>` | `<diagram title>` | <alt/opt branch or separate diagram covering error/timeout/retry/rejected/rollback/recovery> | + +``` +<sequence diagram: primary path, plus error/rollback paths> +``` + +## Rollout / Migration Interlock +<!-- Capture: the ordered cross-repo rollout (toggle timeline, waves, strict ordering). Avoid: enabling before + dependencies ship. Example: "1) ship code flag-OFF 2) enable INT 3) canary prod 4) full." --> +| Step / wave | What ships | Depends on | Toggle state | Owner | +|---|---|---|---|---| + +## Test Strategy +<!-- Capture: the key scenarios this design must prove + link to the full plan. Avoid: duplicating the test + plan here. Example: "must prove: 500-batch < 5s; partial-failure returns per-user status." --> +→ Full plan: `../test-strategy.md`. Key scenarios this design must prove: <list>. + +## Design Coverage Summary +<!-- + Capture: for every concern, mark In-scope / N/A / Out-of-scope, MECHANICALLY derived from the sections above + (In-scope if filled; N/A if marked N/A; Out-of-scope if excluded). Avoid: a concern left blank — that reads + as "silently skipped". Example: "Data model | N/A | no schema change." +--> +| Concern | In-scope / N/A / Out-of-scope | Where addressed | +|---|---|---| +| System context | | System Context | +| Functional decomposition | | Functional-Block Decomposition | +| Object model | | Object-Model Changes | +| Alternatives | | Alternatives Explored | +| Feature toggle | | Feature-Toggle Strategy | +| Scale | | Scale Requirements | +| Service impact | | Service Impact Matrix | +| Interfaces / contracts | | Interface & Contract Definitions | +| Data model | | Data-Model Design | +| Security / RBAC | | Security / RBAC Design | +| HA / failure | | HA & Failure-Condition Matrix | +| Rollout / migration | | Rollout / Migration Interlock | +| Test strategy | | Test Strategy | + +## Reviewer Sign-Off +<!-- Capture: the cross-functional sign-offs needed before implementation-ready. Avoid: marking the design ready + with sign-offs still pending. Example: "Architect | @arch | approved | 2026-06-18." --> +| Role | Reviewer | Status (pending / approved / changes-requested) | Date | +|---|---|---|---| +| Architect | | | | +| Tech Lead | | | | +| Product | | | | +| UX | | | | +| QA | | | | +| Delivery / SRE | | | | + +## References / Traceability +- Feature Spec (WHAT + WHY): `../spec/feature-spec.md` +- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · module docs: manifest-routed, source-local as `<module-path>/ai-docs/<module-name>-spec.md` by default +- Per-interface contracts: `contracts/*.md` (`design/contracts/`) · Test strategy: `../test-strategy.md` +- Decomposition (epics/tasks/implementation plans): `../tasks/` +- Coverage / contracts baseline: `.sdd/manifest.json` diff --git a/.sdd/templates/feature-package/implementation/implementation-plan.template.md b/.sdd/templates/feature-package/implementation/implementation-plan.template.md new file mode 100644 index 000000000..f963c16d7 --- /dev/null +++ b/.sdd/templates/feature-package/implementation/implementation-plan.template.md @@ -0,0 +1,146 @@ +<!-- ─────────────────────────────── + Template: Implementation Plan + Template-ID: implementation-plan + Generates: features/<KEY>/tasks/<epic-slug>/implementation-plan-<n>.md + Description: Decomposition — per-task dev plan: current context, approach, changes, rollback, anticipated PR split. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Implementation Plan — <task title> + +> Start here → repo root [`AGENTS.md`](../../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md). This plan builds its task [`task-<n>.md`](./task-1.md) under epic [`epic.md`](./epic.md); design is [`feature-design.md`](../../design/feature-design.md). (Links relative to this file.) +> Context-efficiency: link to canonical docs — don't duplicate them; ground in current code (file path), don't restate the design. + +<!-- + DECOMPOSITION LAYER 3 of 3. The per-task developer plan: how one PR-sized task will actually be built, + grounded in the current code. Save as features/<KEY>/tasks/<epic-slug>/implementation-plan-<n>.md. + Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition holds. + Each section comment gives Capture / Avoid / Example. Fill from real code (file path) at the current SHA. +--> + +## Task Context +<!-- Capture: the task + parent links + target repos + stream. Avoid: a plan detached from its task. --> +| Field | Value | +|---|---| +| Task | `./task-<n>.md` | +| Parent epic / feature | `./epic.md` · `../../design/feature-design.md` | +| Target repo(s) / module(s) | `<owner/repo>` · `<module>`; repeat per repo when this task is atomic across repos | +| Execution stream / wave | <stream> | +| created_by / date | <provenance> | +| Generated from | `implementation-plan` @ SDLC template library `0.2.2` | + +## Current Context (code-grounded) +<!-- Capture: what the relevant code does TODAY with file path evidence — the baseline this task changes. + Group by target repo when the task spans repos. Avoid describing intended (not actual) behavior. + Example: "<owner/repo> :: <operation>() handles one id @<module>/service.ts." --> +- <current behavior / structure> — evidence: `<file path>` + +## Proposed Approach & Sequencing +<!-- Capture: the ordered steps a reviewer can follow. Avoid: a vague "implement the feature". Example: + "1) add <Request> DTO 2) add service.<operationMany> 3) wire route 4) emit events per item." --> +1. <step> + +<!-- Include if: the task changes a database schema or stored data shape --> +## Schema / Data Changes +<!-- Capture: the table/field change + migration/backfill plan. Avoid: a destructive in-place change. Example: + "add <entity>.<field> (nullable); backfill not needed." --> +- <table/field change; migration + true-up/backfill plan> + +<!-- Include if: the task adds or changes an API / interface --> +## API / Interface Changes +<!-- Capture: the endpoint/signature change + compact request/response shape + schema/API source. Avoid: + changing a response shape silently or pasting a full schema here. Example: + "POST /<resource>:<operation> -> {results:[{id,status}]}; openapi.yaml#/paths/... ." --> +- <endpoint/signature change; compact shape; schema/API source> + +<!-- Include if: the task changes a cross-service contract --> +## Contract Changes +<!-- Capture: the Provides/Requires delta + links to the contract doc, schema/API source, module summary, and + root index. Avoid: an undocumented contract change. Example: + "Provides ADDED <operation> -> design/contracts/<operation>.md; openapi.yaml#/paths/...; + update <module-path>/ai-docs/<module-name>-spec.md Public Surface and ai-docs/CONTRACTS.md." --> +- <Provides/Requires delta; links to `../../design/contracts/*.md`, schema/API source, module spec summary, and root `CONTRACTS.md` row> + +<!-- Include if: the task touches a security or compliance surface --> +## Security / Compliance (task-scoped) +<!-- Capture: the authz/validation/secret handling for THIS task. Avoid: skipping authz on a privileged op. + Example: "verify privileged scope per target boundary before applying the operation." --> +- <authz, validation, secret handling, data classification for this task> + +<!-- Include if: the task affects build, CI, or deployment --> +## Build / CI / Deployment Impact +<!-- Capture: new build/test steps, pipeline/config changes. Avoid: a new test tier with no CI wiring. Example: + "adds an integration test stage gated on a test datastore." --> +- <new build/test steps, pipeline or config changes> + +## Backward-Compat / Rollback +<!-- Capture: how existing callers/data keep working + how to back it out safely. Avoid: an irreversible change + with no rollback. Example: "flag-gated; disable the flag to revert; no data migration to undo." --> +- **Compatibility:** <how existing callers/data keep working> +- **Consumer transition / deprecation:** <if a public surface changes incompatibly, how consumers move> +- **Rollback:** <how to revert; is it safe mid-rollout?> + +## Logs / Metrics / Alerting +<!-- Capture: the observability this task adds/relies on. Avoid: shipping a new path with no signal. Example: + "log per-item result at debug; metric <operation>_total." --> +- <logs/metrics/alerts to add or update> + +## Implementation Caveats +<!-- Capture: gotchas — idempotency, ordering/sequencing, eventual consistency, edge cases. Avoid: ignoring + partial-failure. Example: "partial failure: return per-item status; do not roll back the whole batch." --> +- <caveat> + +## Anticipated PR Split +<!-- Capture: how the task breaks into PR(s), one logical concern each. Avoid: one giant PR mixing concerns. + Example: "PR1 service+tests; PR2 route+events." --> +| PR | Scope (one logical concern) | Depends on | +|---|---|---| + +## Manual Validation +<!-- Capture: hand checks before/after merge beyond automated tests. Avoid: relying only on unit tests for a + user-facing path. Example: "disable 3 users via the admin UI; confirm events on the bus." --> +- [ ] <manual check> + +## AI Docs Impact +<!-- Capture: decide every canonical AI doc or schema source this task must update before code is written. + Use one matrix per target repo/component. For a multi-repo task, repeat this section for each repo + named in the task's Multi-Repo Scope. Avoid one combined matrix that hides which repo owns the docs. + Use "not required — <reason>" instead of leaving a row blank. If a required destination is + missing/conflicting/protected, mark "blocked" and name the pre-work. --> + +### <owner/repo> — <module/component> +| Field | Value | +|---|---| +| Manifest | `.sdd/manifest.json` | +| Standing docs root | `ai-docs/` | + +| Doc / source | Decision (required / not required / blocked) | Reason / trigger | Required update or no-impact reason | +|---|---|---|---| +| Touched module spec(s) | <required / not required / blocked> | <behavior, requirement, public surface, invariant, flow, state, protocol, UI, data, or test-strategy impact> | `<module-path>/ai-docs/<module-name>-spec.md` / <reason> | +| `ai-docs/SPEC_INDEX.md` | <required / not required / blocked> | <module registry, responsibility, canonical spec path, coverage mirror, docs routing, task routing, or standing-doc location impact> | <row/update or reason> | +| `ai-docs/CONTRACTS.md` | <required / not required / blocked> | <endpoint/export/event/command/RPC/schema/Requires impact> | <row/update or reason> | +| Native schema/API source | <required / not required / blocked> | <OpenAPI/AsyncAPI/proto/GraphQL/JSON Schema/SDK/API report/package entry point impact> | <source update or reason> | +| `ai-docs/SERVICE_STATE.md` | <required / not required / blocked> | <current endpoint/event/store/dependency/limit/metric/flag impact> | <row/update or reason> | +| `ai-docs/DATA_MODEL.md` | <required / not required / blocked> | <entity, ownership, relationship, migration, retention, cache-backed data impact> | <row/update or reason> | +| `ai-docs/SECURITY.md` | <required / not required / blocked> | <trust boundary, authn/authz, secrets, data classification, validation, encoding, session, risk impact> | <section/update or reason> | +| `ai-docs/GLOSSARY.md` | <required / not required / blocked> | <domain term, entity, event, state, or public concept impact> | <term/update or reason> | +| `ai-docs/ARCHITECTURE.md` | <required / not required / blocked> | <component responsibility, interaction, flow, dependency, infra, cross-repo, cross-cutting impact> | <section/update or reason> | +| `ai-docs/RULES.md` | <required / not required / blocked> | <enforceable convention, review, test, logging, error, security, or drift-threshold impact> | <rule/update or reason> | +| README / public API / help / release docs | <required / not required / blocked> | <user/developer-facing documentation impact> | <doc/update or reason> | + +## Documentation Updates +<!-- Capture: the docs/specs that must change in the same change (spec-currency). Avoid: merging without doc + updates. Group by target repo and populate this from every AI Docs Impact row marked "required". + Example: "update the owning module spec or design/contracts/<interface>.md, then add or adjust the + ai-docs/CONTRACTS.md index pointer if the stable surface changes." --> +### <owner/repo> +- <owning module Public Surface summary> +- <ai-docs/SPEC_INDEX.md row if routing/registry changes> +- <root ai-docs/CONTRACTS.md index row> +- <canonical schema/API source, if changed> +- <SERVICE_STATE / DATA_MODEL / SECURITY / GLOSSARY / ARCHITECTURE / RULES update, if required> +- <spec / README / API doc / help guide to update> + +## References +- Task: `./task-<n>.md` · Epic: `./epic.md` · Feature design: `../../design/feature-design.md` diff --git a/.sdd/templates/feature-package/intake/bug-intake.template.md b/.sdd/templates/feature-package/intake/bug-intake.template.md new file mode 100644 index 000000000..0b48507df --- /dev/null +++ b/.sdd/templates/feature-package/intake/bug-intake.template.md @@ -0,0 +1,43 @@ +<!-- ------------------------------------------------- + Template: Bug Intake + Template-ID: bug-intake + Generates: .generated/sdd/features/<KEY>/run-records/intake-questionnaire.md + Description: Code-grounded intake questions for a defect or behavior correction. + Library ver: 0.2.2 + Last updated: 2026-06-30 +-------------------------------------------------- --> + +# Bug Intake + +> Start here -> repo root [`AGENTS.md`](../../../AGENTS.md) (agent entry) and router +> [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md). Links assume the standard `features/<KEY>/` layout. +> Context-efficiency: link to canonical docs; the filled record lands in +> `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` and is provenance, not a canonical spec. + +<!-- + Capture current behavior, expected behavior, and invariants before a fix is designed. Every answer + should be validated against code, tests, logs, tickets, or an explicitly recorded human decision. +--> + +## Metadata + +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Intake type | defect / behavior correction | +| Generated from | `bug-intake` @ SDLC template library `0.2.2` | + +## Questions + +1. Affected module(s)? Validate against code and existing routing docs. +2. Current behavior: what actually happens, with a specific repro when possible. +3. Expected behavior: what should happen instead. +4. What must NOT change: at least one invariant the fix must preserve. +5. Severity: SEV-1 / SEV-2 / SEV-3 / SEV-4, with one-line justification. +6. Change class: routine / security / contract-affecting / perf-critical / persistence / ui. +7. Characterization baseline: what existing test, log, or fixture pins current behavior? + +## Output + +The filled run record captures verified facts, unresolved conflicts, must-not-change invariants, +change class, and any required characterization baseline. diff --git a/.sdd/templates/feature-package/intake/feature-intake.template.md b/.sdd/templates/feature-package/intake/feature-intake.template.md new file mode 100644 index 000000000..d8487ec1a --- /dev/null +++ b/.sdd/templates/feature-package/intake/feature-intake.template.md @@ -0,0 +1,70 @@ +<!-- ------------------------------------------------- + Template: Feature Intake + Template-ID: feature-intake + Generates: .generated/sdd/features/<KEY>/run-records/intake-questionnaire.md + Description: Code-grounded intake questions for a new feature. + Library ver: 0.2.2 + Last updated: 2026-06-30 +-------------------------------------------------- --> + +# Feature Intake + +> Start here -> repo root [`AGENTS.md`](../../../AGENTS.md) (agent entry) and router +> [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md). The intake's change-class output gates conditional +> sections of the Feature Spec. Links assume the standard `features/<KEY>/` layout. +> Context-efficiency: link to canonical docs; the filled record lands in +> `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` and is provenance, not a canonical spec. + +<!-- + Question content for a new feature. The caller owns the mechanics: build a prior from code, ask + bounded questions, validate answers against evidence, branch/prune, and record decisions. + Ask only about fact categories that apply to the change. Do not force API, event, schema, storage, + UI, or dependency questions when evidence shows that category is not touched. Use file paths and + generated references as evidence only; ask the developer about the repo impact in plain language. +--> + +## Metadata + +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Intake type | feature | +| Generated from | `feature-intake` @ SDLC template library `0.2.2` | + +## Questions + +1. Module set: are these the modules this feature touches? `<list>` (yes/no) -- validated against code. +2. Documentation readiness: for each touched area, can agents rely on its existing module docs, should + they cross-check code, or is code the only reliable source until docs are backfilled? +3. WHAT: one sentence in user language. +4. WHY: the problem or goal. +5. In scope / out of scope: confirm both; out of scope must be non-empty. +6. Acceptance signals: observable and testable outcomes. +7. Risk areas: does this touch security, a consumer-facing interface, performance, stored data, + user-visible behavior, external services, events/messages, package release behavior, or only + internal implementation? If none apply, say it is a routine internal change. + +*Include if: the feature changes an exposed or consumed interface* +- What interface changes: API/export, endpoint, command, event/message, schema, file format, package + surface, or external service dependency? If no exposed/consumed interface changes, mark this not + applicable from evidence. + +*Include if: the feature changes stored data* +- New or changed schema, table, migration, or stored data shape? + +*Include if: the feature changes a user-visible flow* +- What user-visible flow changes? Is there a design reference? + +*Include if: the feature changes a published package's public surface* +- What public API changes, and what is the semver impact? + +*Include if: the feature touches a network/HTTP API* +- What endpoint or external dependency changes? + +*Include if: the feature publishes or consumes events* +- What event/topic/consumer/producer contract changes? + +## Output + +The filled run record captures verified-from-code facts, verified developer facts, source-readiness, +unknowns, resolved conflicts, blocking conflicts, confidence, and resume state. diff --git a/.sdd/templates/feature-package/intake/module-spec-intake.template.md b/.sdd/templates/feature-package/intake/module-spec-intake.template.md new file mode 100644 index 000000000..0499247ef --- /dev/null +++ b/.sdd/templates/feature-package/intake/module-spec-intake.template.md @@ -0,0 +1,66 @@ +<!-- ------------------------------------------------- + Template: Module Spec Intake + Template-ID: module-spec-intake + Generates: .generated/sdd/features/<KEY>/run-records/intake-questionnaire.md + Description: Code-grounded intake questions for a new module or a deeper module spec. + Library ver: 0.2.2 + Last updated: 2026-06-30 +-------------------------------------------------- --> + +# Module Spec Intake + +> Start here -> repo root [`AGENTS.md`](../../../AGENTS.md) (agent entry) and router +> [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md). Links assume the standard `features/<KEY>/` layout. +> Context-efficiency: link to canonical docs; the filled record lands in +> `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` and is provenance, not a canonical spec. + +<!-- + Capture the module boundary, any exposed/consumed surfaces that actually apply, dependencies, and + which conditional sections belong in `<module-path>/ai-docs/<module-name>-spec.md`. Do not invent API/contract questions for + modules that have no API, event, command, UI, schema, file, package, or external system boundary. +--> + +## Metadata + +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Intake type | module-spec | +| Generated from | `module-spec-intake` @ SDLC template library `0.2.2` | + +## Questions + +1. Module name and one-line responsibility. +2. Topology fit: where does this module sit in the repository? +3. Exposed surface: if this module is consumed by other code, users, jobs, or systems, what does it + expose: API/export, endpoint, command, event/message, UI flow, schema, file format, package + surface, or external service behavior? If none exists, mark it not applicable from evidence. +4. Dependencies: what other modules, services, stores, contracts, or runtime resources does it rely on? +5. Primary operations: what are the main operations or flows this module supports? +6. Failure and recovery: what errors, timeout paths, retries, rejected operations, or recovery signals + can callers or maintainers observe? +7. Internal structure: what are the main classes/components/functions and how do they collaborate? +8. Data movement: what inputs enter the module, what transformations happen, and what outputs/events + leave it? +9. Use cases: what actor or caller flows should future agents understand before changing this module? +10. Tests and gaps: which tests prove current behavior, and where are positive/negative or edge-case + tests missing? +11. Source material: are there existing overview, architecture, HLD, LLD, API, or test notes that + should be used as source material for the canonical module spec? +12. Which deeper sections apply from evidence or developer confirmation? + - [ ] state machine + - [ ] protocol / wire format + - [ ] UI flow + - [ ] data model + - [ ] concurrency / reactive flow + - [ ] caller-visible error handling + - [ ] module-specific conventions + - [ ] export stability + - [ ] host integration + +## Output + +The filled run record captures the selected module boundary, public surface, dependencies, primary +operations, failure/recovery paths, internal relationships, data movement, use cases, tests/gaps, +source material, and section choices. `ai-docs/SPEC_INDEX.md` routes to the canonical module spec; +module-specific detail stays inside `<module-path>/ai-docs/<module-name>-spec.md` by default. diff --git a/.sdd/templates/feature-package/spec/feature-spec.template.md b/.sdd/templates/feature-package/spec/feature-spec.template.md new file mode 100644 index 000000000..2893bc81f --- /dev/null +++ b/.sdd/templates/feature-package/spec/feature-spec.template.md @@ -0,0 +1,238 @@ +<!-- ─────────────────────────────── + Template: Feature Spec + Template-ID: feature-spec + Generates: features/<KEY>/spec/feature-spec.md + Description: Per-feature capture — WHAT+WHY, scope, acceptance, success/guardrail metrics, contracts delta, requirements state. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Feature Spec — <feature title> + +> Start here → repo root [`AGENTS.md`](../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). Then this spec → design [`feature-design.md`](../design/feature-design.md) → test plan [`test-strategy.md`](../test-strategy.md). (Links relative to `features/<KEY>/spec/`; the root may be a workspace-level `AGENTS.md`.) +> Context-efficiency: link to canonical docs — don't duplicate them; capture product intent here, the design lives in feature-design. + +<!-- + Per-feature spec (instantiated during Capture and refined during Discovery). Captures PRODUCT intent (WHAT+WHY) — + no architecture (that's the Feature Design). Headings are flat; sections preceded by `<!-- Include if: + ... -->` are kept only when the change class or the stated condition fires. + Where a universal section doesn't apply, write the heading + "N/A — <reason>" so Spec State shows nothing + was skipped by accident. Each section comment gives Capture / Avoid / Example. +--> + +## Metadata +<!-- Capture: identity + status + change class + provenance. Avoid: leaving change class blank (it gates the + conditional sections). Example: "Change class: contract-affecting + perf-critical." --> +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Title | <title> | +| Status | draft / groomed / architected / tasked / implementation-ready | +| Change class | <routine / security / contract-affecting / perf-critical / persistence / ui> — from questionnaire | +| created_by / approved_by / date | <provenance> | +| Generated from | `feature-spec` @ SDLC template library `0.2.2` | + +## Problem & Goal (WHAT + WHY) +<!-- Capture: the user-language WHAT and the problem/business WHY. Avoid: stating a solution as the problem, or + omitting WHY. Example: "What: let operators perform `<bulk operation>`. Why: current one-by-one workflow is too slow during incidents." --> +**What:** <user-language statement of what's being built> +**Why:** <the problem it solves / business goal — required> + +## Stakeholders & Open Questions +<!-- Capture: who must be consulted/sign off, and every blocking open question WITH an owner. Avoid: an open + question with no owner (it gets silently dropped). Example: "Q-1 retention window? owner: @legal; blocks: schema." --> +| Stakeholder / role | Interest in this feature | Sign-off needed? | +|---|---|---| +| <role> | <why they care> | yes / no | + +**Open questions:** +- **Q-1 <question>** — owner: <who> — blocks: <what it gates> — status: open / answered + +## Scope +<!-- Capture: in-scope + out-of-scope (non-empty!) + open product decisions. Avoid: an empty out-of-scope (the + #1 cause of scope creep). Example: "Out: no API change; no migration of historical data." --> +**In scope:** <bullets> +**Out of scope:** <bullets — MUST NOT be empty> +**Open decisions:** <unresolved PM/arch points, or "none"> + +## Requirements +<!-- Capture: numbered requirements, each with a stable ID, rationale, acceptance, and STATE (Draft → Agreed → + Implemented → Verified / Dropped). Avoid: requirements with no ID (tasks/tests can't trace to them). + Example: "R-1 | process up to <limit> items | operational speed | returns per-item result | Agreed." --> +| Req ID | Requirement (WHAT) | Rationale (WHY) | Acceptance (how proven) | State | +|---|---|---|---|---| +| R-1 | <statement> | <why> | <observable/testable condition> | Draft / Agreed / Implemented / Verified | + +## Acceptance Criteria +<!-- Capture: the observable, testable conditions that close the feature, referencing Req IDs. Avoid: subjective + criteria ("works well"). Example: "Processing <limit> items returns in <latency bound> and emits one event each (R-1)." --> +- <observable, testable condition> (R-<n>) + +## Success & Guardrail Metrics +<!-- Capture: success metrics (intended improvement) AND guardrail metrics (must-not-regress bounds). Avoid: + only success metrics — guardrails are what tell an agent what it may NOT break. Example: "success: workflow + time ↓80%; guardrail: existing error rate stays below the agreed bound." --> +| Metric | Type (success / guardrail) | Baseline | Target / bound | How measured | +|---|---|---|---|---| +| <metric> | success / guardrail | <value> | <value> | <source / dashboard> | + +## Prior-Work Register +<!-- Capture: existing code/features/specs related to this change + whether to reuse/extend/supersede. Avoid: + reinventing an existing capability. Example: "<existing-capability>/ | already supports related operation | extend." --> +| Existing artifact (path / feature / spec) | How it relates | Reuse / extend / supersede | +|---|---|---| +| `<path or feature>` | <relationship> | reuse / extend / supersede | + +## Contracts Delta +<!-- Ownership: this section captures only the product-level contract DELTA. Full interface schema lives + in ../design/contracts/ and the native schema/API source when one exists. The owning module spec + summarizes the stable surface, and root ai-docs/CONTRACTS.md indexes it; neither is a full schema dump. --> +<!-- Capture: Provides/Requires as a DELTA (ADDED/MODIFIED/REMOVED) vs the module's current baseline. Avoid: + restating the whole surface. Include the schema/detail source when known. Example: + "Provides: ADDED POST /<resource>:<operation>; schema openapi.yaml#/paths/... ." --> +**Provides:** ADDED/MODIFIED/REMOVED <interface / event / endpoint + guarantees> +**Requires:** ADDED/MODIFIED/REMOVED <dependency + availability/fallback> + +## Impacted Modules / Repos +<!-- Capture: each module/repo touched + the impact + its manifest coverage state. Avoid: a vague "various". + Example: "`<module>/` | adds `<operation>` surface | manifest state." --> +| Module / repo | Impact | Manifest coverage state | +|---|---|---| +| `<module>` | <one line> | <from `.sdd/manifest.json`> | + +<!-- ===== Conditional sections — keep a section only when its Include-if condition holds ===== --> + +<!-- Include if: the feature is non-trivial or technically uncertain — capture feasibility before committing [condition-id: feature.feature_nontrivial] --> +## Feasibility & Risks +<!-- Capture: can it be built as scoped (constraints/unknowns), spikes needed, and the risk table. Avoid: + committing with an unexamined unknown. Example: "Spike: confirm `<operation>` can process <limit> items within the target boundary." --> +- **Feasibility:** <can it be built as scoped? known constraints / unknowns> +- **Spikes needed:** <investigations to de-risk, or "none"> +| Risk | Likelihood | Impact | Mitigation | +|---|---|---|---| +| <risk> | low/med/high | low/med/high | <mitigation> | + +<!-- Include if: the feature interacts with other features/states (toggles, modes, concurrent flows) [condition-id: feature.feature_interactions] --> +## Interaction / Scenario Matrix +<!-- Capture: the condition/state combinations the feature must behave correctly across + expected outcome + + coverage. Avoid: only the happy combination. Example: "item already in target state × repeated operation → no-op, no error." --> +| Scenario / condition combination | Expected behavior | Covered by (Req / test) | +|---|---|---| +| <condition × state> | <expected> | R-<n> / <test> | + +<!-- Include if: the change touches data shapes / required fields [condition-id: feature.touches_data_shapes] --> +## Product-Requested Data / Fields +<!-- Capture: each new/changed field + its meaning/requirement. Avoid: a field with no stated purpose. --> +| Field | Meaning / requirement | +|---|---| + +<!-- Include if: backward-compat / coexistence / rollout is in scope [condition-id: feature.backward_compat] --> +## Migration Expectations +<!-- Capture: the cutover/coexistence rule. Avoid: a breaking cutover with no coexistence window. Example: + "old + new paths coexist for one release; flag default OFF." For public-surface changes, this is the + product-level consumer transition expectation; exact contract details live in design/contracts/. --> +- <rule / cutover expectation> + +<!-- Include if: the change is perf-critical [condition-id: feature.perf_critical] --> +## Scale & Performance +<!-- Ownership: product-level performance requirement only. The technical scale design and capacity + approach live in feature-design.md. --> +<!-- Capture: the throughput/latency requirement. Avoid: numbers copied from another feature. Example: + "bulk op: 500 users < 5s p99." --> +| Metric | Requirement | +|---|---| +| Throughput / latency p90/p99 | <value> | + +<!-- Include if: the change touches a security or compliance surface [condition-id: feature.security_compliance] --> +## Compliance & Security +<!-- Ownership: product/security obligations only. The technical authz/RBAC and trust-boundary design + lives in feature-design.md; standing posture lives in SECURITY.md. --> +<!-- Capture: the validation the change needs (pen-test/SAST/PII review). Avoid: skipping review on an authz + change. Example: "authorization review — `<operation>` must check privileged scope per target boundary." --> +- <validation needed: pen-test / SAST / DAST / PII review …> + +<!-- Include if: the feature is flagged or needs staged rollout [condition-id: feature.needs_rollout] --> +## Rollout & Flags +<!-- Ownership: product rollout expectation only. Toggle mechanics and rollout interlock live in + feature-design.md. --> +<!-- Capture: each flag, its purpose, safe default, new/existing. Avoid: a flag defaulting ON before rollout. + Example: "<operationFlag> | gate new endpoint | OFF | new." --> +| Flag | Purpose | Default | New/Existing | +|---|---|---|---| + +<!-- Include if: serviceability matters — new logs/metrics/alerts [condition-id: feature.serviceability] --> +## Serviceability +<!-- Capture: the logs/metrics/dashboards/alerts to add. Avoid: shipping with no signal for the new path. + Example: "metric users_bulk_disabled_total; alert on error-rate spike." --> +- Logs / metrics / dashboards / alerts to add. + +<!-- Include if: documentation is part of the release contract [condition-id: feature.doc_obligations] --> +## Documentation Obligations +<!-- Capture: the docs that must ship with the feature. Avoid: an API change with no doc update. Example: + "update the admin help guide + the public API reference." --> +- <help guide / API docs / privacy data sheet …> + +<!-- Include if: the feature changes a user-visible screen/flow [condition-id: feature.changes_ui] --> +## UI Flow & Design +<!-- Ownership: user-visible requirement only. Detailed UX and backing interface mapping live in + feature-design.md. --> +<!-- Capture: the user-visible flow + Figma link + whether it changes an existing flow. Avoid: a UI change with + no design reference. Example: "adds a bulk-select bar to the users table; Figma <link>." --> +- User-visible flow (screens/states); **Figma:** <link>. Does this change an existing flow? <y/n> + +<!-- Include if: the feature changes a network/HTTP API [condition-id: feature.changes_api] --> +## API / Event Contract +<!-- Ownership: exact delta and links only. Full schema lives in ../design/contracts/. Stable catalog + lives in ai-docs/CONTRACTS.md as an index/pointer. Native schema files such as OpenAPI/AsyncAPI + `.yaml`, `.proto`, `.graphql`, JSON Schema, or SDK API reports are the exact machine-readable source. --> +<!-- Capture: the exact interface contract(s) + link to the per-interface contract doc + whether dev-portal + review is needed. Avoid: inventing a route shape or pasting full schemas. Example: + "POST /<resource>:<operation> -> contracts/<operation>.md; schema openapi.yaml#/paths/... ." --> +- Exact interface contract(s); link to `contracts/*.md` and schema/API source. API registry/dev-portal review needed? <y/n> + +<!-- Include if: the feature publishes or consumes events [condition-id: feature.changes_events] --> +## Event Contract +<!-- Capture: published/consumed event deltas + schema/ordering effects. Avoid: changing a payload silently. + Example: "UserDisabled gains `reason`; backward-compatible additive field." --> +- Published/consumed event deltas; payload schema source; ordering/delivery guarantees affected? + +<!-- Include if: the feature changes a published package's public surface [condition-id: feature.changes_public_api] --> +## Public API & Semver Impact +<!-- Capture: the exported symbols changed + the semver impact + API detail source. Avoid: a breaking export in + a minor bump or inventing custom YAML for SDK APIs. Example: "adds optional arg -> minor; API report + docs/api-report.md." --> +- Exported symbols/types changed; semver impact (major/minor/patch); API detail source? + +<!-- Include if: this is a monorepo and the change spans packages [condition-id: feature.cross_package] --> +## Cross-Package Impact +<!-- Capture: which packages change + inter-package contract effects. Avoid: a cross-package change with no + version-sync note. --> +- Which packages change; inter-package contract effects. + +## Spec State +<!-- Capture: mark each section complete/partial/pending/N-A so capture can stop and resume. Avoid: leaving it + stale vs the sections above. Example: "Requirements: partial; Metrics: pending." --> +| Section | State (complete / partial / pending / N/A) | +|---|---| +| Problem & Goal | | +| Stakeholders & Open Questions | | +| Scope | | +| Requirements | | +| Acceptance Criteria | | +| Success & Guardrail Metrics | | +| Prior-Work Register | | +| Contracts Delta | | +| Impacted Modules | | +| Conditional sections (as triggered) | | + +## Change Log +<!-- Capture: dated material changes — who/what/why. Version control retains detailed file changes. Avoid: + rewriting history instead of appending. Example: "2026-06-18 | scope cut: drop CSV import | @pm | de-risk v1." --> +| Date | Change | By | Why | +|---|---|---|---| +| <YYYY-MM-DD> | <what changed> | <who> | <rationale> | + +## References +- Feature design: `../design/feature-design.md` · decomposition: `../tasks/` +- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · module docs: manifest-routed, source-local as `<module-path>/ai-docs/<module-name>-spec.md` by default +- Test plan: `../test-strategy.md` · intake: `.generated/sdd/features/<KEY>/run-records/intake-questionnaire.md` diff --git a/.sdd/templates/feature-package/tasks/epic.template.md b/.sdd/templates/feature-package/tasks/epic.template.md new file mode 100644 index 000000000..7467d0b09 --- /dev/null +++ b/.sdd/templates/feature-package/tasks/epic.template.md @@ -0,0 +1,85 @@ +<!-- ─────────────────────────────── + Template: Epic + Template-ID: epic + Generates: features/<KEY>/tasks/<epic-slug>/epic.md + Description: Decomposition — a coherent slice of the design: mapped sections, child tasks, sequencing, exit criteria. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Epic — <epic title> + +> Start here → repo root [`AGENTS.md`](../../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md). This epic delivers a slice of the design [`feature-design.md`](../../design/feature-design.md); its child tasks ↔ [`task-<n>.md`](./task-1.md) → impl plan. (Links relative to this file.) +> Context-efficiency: link to canonical docs — don't duplicate them; map to design sections, don't restate them. + +<!-- + DECOMPOSITION LAYER 1 of 3 (Epic → Task → Implementation Plan). An epic is a coherent slice of the feature + design — typically one per service group or capability. Save as features/<KEY>/tasks/<epic-slug>/epic.md. + Headings are flat; sections preceded by `<!-- Include if: ... -->` are kept only when the condition holds. + Each section comment gives Capture / Avoid / Example. +--> + +## Metadata +<!-- Capture: identity + the slice it owns + tracker key + status. Avoid: an epic that spans unrelated + capabilities. Example: "Service group / capability: <resource> bulk operations." --> +| Field | Value | +|---|---| +| Epic title | <title> | +| Parent feature | `../../design/feature-design.md` (and `../../spec/feature-spec.md`) | +| Service group / capability | <the coherent slice this epic owns> | +| Tracker / Epic key | <key/URL, or "TBD — not projected to the tracker"> | +| Status | draft / ready / in-progress / done | +| created_by / approved_by / date | <provenance> | +| Generated from | `epic` @ SDLC template library `0.2.2` | + +## Scope — the slice of the design this epic delivers +<!-- Capture: in one paragraph, the coherent capability this epic owns. Avoid: restating the whole feature. + Example: "Everything for `<bulk operation>` in `<module>` — endpoint, service, events." --> +<scope> + +## Mapped Design Sections +<!-- Capture: which Feature_Design sections this epic implements (design→epic traceability). Avoid: an epic with + no mapped design. Example: "Functional decomposition: <OperationBlock> block → this epic." --> +| Feature design section | What this epic delivers from it | +|---|---| +| <e.g. Functional-Block Decomposition: block X> | <deliverable> | + +## Summary of Changes (per service / module) +<!-- Capture: the high-level change per service/module touched. Avoid: file-level detail (that's the task/plan). + Example: "`<module>/`: add `<operation>` endpoint + service method + `<DomainEvent>` event." --> +- **`<service / module>`:** <change summary> + +<!-- Include if: this is a brownfield/incremental epic where a baseline already shipped --> +## Baseline vs This Epic +<!-- Capture: what already exists vs what this epic adds. Avoid: re-building shipped capability. Example: + "single-item operation: shipped; bulk path: this epic." --> +| Capability | Already shipped (baseline) | Added by this epic | +|---|---|---| + +## Child Tasks +<!-- Capture: the PR-sized tasks under this epic, each linked. Avoid: a task too big for one PR. Example: + "T1 endpoint+service | yes | task-t1.md." --> +| Task | One-line | PR-sized? | Task doc | Tracker key | +|---|---|---|---|---| +| T1 | <summary> | yes | `task-t1.md` | <key/TBD> | + +## Sequencing & Dependencies +<!-- Capture: execution order across tasks/sibling epics — what blocks what, what's parallel-safe. Avoid: + hidden ordering that causes a broken intermediate state. Example: "T2 (events) blocked-by T1 (service)." --> +| Task / epic | Depends on | Parallel-safe with | Wave | +|---|---|---|---| + +<!-- Include if: this epic has a specific ordered rollout across steps/repos --> +## Rollout Order +<!-- Capture: the ordered rollout steps. Avoid: enabling the flag before the code ships. Example: "1) ship code + (flag OFF) 2) enable in integration 3) prod-enable." --> +1. <ordered step> + +## Exit Criteria +<!-- Capture: the observable conditions that mark the epic done (distinct from per-task acceptance). Avoid: + "all tasks done" with no verifiable outcome. Example: "[ ] bulk endpoint live behind flag in prod." --> +- [ ] <criterion> + +## References +- Feature design: `../../design/feature-design.md` · Feature spec: `../../spec/feature-spec.md` +- Child tasks: `./task-*.md` · Implementation plans: `./implementation-plan-*.md` diff --git a/.sdd/templates/feature-package/tasks/task.template.md b/.sdd/templates/feature-package/tasks/task.template.md new file mode 100644 index 000000000..9206624c5 --- /dev/null +++ b/.sdd/templates/feature-package/tasks/task.template.md @@ -0,0 +1,114 @@ +<!-- ─────────────────────────────── + Template: Task + Template-ID: task + Generates: features/<KEY>/tasks/<epic-slug>/task-<n>.md + Description: Decomposition — a PR-sized task with an ownership boundary, acceptance + verifier-exit criteria, traceability. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Task — <task title> + +> Start here → repo root [`AGENTS.md`](../../../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md). This task sits under its epic [`epic.md`](./epic.md) ↔ its plan [`implementation-plan-<n>.md`](./implementation-plan-1.md); it implements part of [`feature-design.md`](../../design/feature-design.md). (Links relative to this file.) +> Context-efficiency: link to canonical docs — don't duplicate them; reference the design section this task implements. + +<!-- + DECOMPOSITION LAYER 2 of 3. A PR-sized unit under an epic — small enough for one reviewable PR. Its defining + feature is an explicit OWNERSHIP BOUNDARY so parallel tasks don't collide. Save as + features/<KEY>/tasks/<epic-slug>/task-<n>.md. Headings are flat; sections preceded by `<!-- Include if: + ... -->` are kept only when the condition holds. Each section comment gives Capture / Avoid / Example. +--> + +## Metadata +<!-- Capture: identity, task type, target repo/module, tracker key, state. Avoid: a "task" that's really a whole + epic. Example: "Task type: API; Target: <module>; State: not_started." --> +| Field | Value | +|---|---| +| Task title | <title> | +| Parent epic | `./epic.md` | +| Parent feature | `../../design/feature-design.md` (and `../../spec/feature-spec.md`) | +| Task type | backend / API / migration / validation / docs / observability / security / rollout / UI | +| Target repo(s) / module(s) | `<owner/repo>` · `<module>`; repeat per repo when this task is atomic across repos | +| Tracker key | <key/URL, or "TBD — not projected"> | +| State | not_started / in_progress / ready_for_merge / merged | +| created_by / approved_by / date | <provenance> | +| Generated from | `task` @ SDLC template library `0.2.2` | + +## Source Mapping +<!-- Capture: the exact feature-design (or epic) section this task implements — why it exists. Avoid: a task + with no design link. Example: "Implements Feature Architecture → <OperationBlock> block." --> +- Implements: <Feature_Design section path> → <what part> + +## Primary Code Touchpoints +<!-- Capture: the files/modules this task changes, grounded in real paths and grouped by target repo when + more than one repo is in scope. Avoid: vague "the users area". Example: + "<owner/repo> :: <module>/api/<operation>.ts (new route)". --> +- `<path/file>` — <what changes> + +## Ownership Boundary +<!-- Capture: the files/regions THIS task owns so two PR-sized tasks never edit the same lines, and what it must + NOT touch. Group by target repo when more than one repo is in scope. Avoid: overlapping boundaries + between sibling tasks. Example: "Owns <repo-a>:<module>/api/<operation>.ts; must NOT touch + <repo-a>:<module>/events/* (owned by T2)." --> +- Owns: `<path or path#region>` +- Must NOT touch (owned by another task): `<path>` → owned by `<task>` + +## Multi-Repo Scope +<!-- Capture: use "N/A — single target repo" for normal repo-scoped tasks. If this task spans repos, state why + it is one atomic task instead of separate repo-scoped tasks, and list each repo's manifest plus + standing-docs root. Module specs remain source-local under each module path. --> +| Target repo | Module/component | Why included in this task | Manifest / standing docs root | +|---|---|---|---| +| `<owner/repo>` | `<module>` | <reason / N/A for single repo> | `.sdd/manifest.json` / `ai-docs/` | + +## Dependencies / Execution Stream +<!-- Capture: blocked-by / parallel-safe / wave. Avoid: claiming parallel-safe when boundaries overlap. Example: + "blocked-by T1; parallel-safe with T3; wave 2." --> +| This task | Depends on (blocked-by) | Parallel-safe with | Wave / stream | +|---|---|---|---| + +## Acceptance Criteria +<!-- Capture: task-specific, observable, testable conditions. Avoid: restating the feature's acceptance. Example: + "[ ] POST /<resource>:<operation> returns a per-item result array." --> +- [ ] <criterion> + +## Verifier Exit Criteria +<!-- Capture: the pass/fail checks an INDEPENDENT verifier (different runtime than the implementer) runs — how + it's PROVEN correct, distinct from acceptance. Avoid: duplicating acceptance. Example: "[ ] changed-line + coverage ≥ 80%; [ ] no contract drift vs ai-docs/CONTRACTS.md." --> +- [ ] <check the verifier must confirm> + +## Traceability +<!-- Capture: requirement/rule → code symbol → test, so each change traces back to intent and forward to a test. + Avoid: a requirement with no test. Example: "R-1 | <OperationService>.<method> | <operation>.spec.ts: processes <limit>." --> +| Requirement / rule id | Code symbol (class.method / file) | Test that proves it | +|---|---|---| + +## Coverage Expectation +<!-- Capture: the changed-line coverage threshold + where evidence lives. Avoid: no coverage target on new code. + Example: "≥ 80%; evidence: CI coverage report." --> +- Changed-line coverage ≥ <threshold>%; evidence: <report path / CI check> + +## Cross-Cutting Prompts +<!-- Capture: answer each (or mark N/A) — don't skip silently. Avoid: leaving security/idempotency blank on a + write path. Example: "Idempotency: re-applying an already-applied operation is a no-op." --> +- **Logging:** <what to log / N/A> +- **Metrics:** <what to emit / N/A> +- **Security:** <authz/validation/secret handling / N/A> +- **Idempotency:** <retry/replay safety / N/A> +- **Rollout:** <flag/sequencing assumption / N/A> + +## Non-Goals / Out-of-Scope +<!-- Capture: what this task explicitly does NOT do (prevents creep into sibling tasks). Avoid: an empty + non-goals on a task adjacent to others. Example: "Does NOT add the UI — that's T4." --> +- <out-of-scope item> + +<!-- Include if: this task is gated by a feature flag or has rollout assumptions --> +## Feature-Flag & Rollout Assumptions +<!-- Capture: the flag, default, and what must be true to deploy safely. Avoid: assuming the flag is already ON. + Example: "Flag <operationFlag> default OFF; enable only after T2 events ship." --> +- Flag: `<name>` · default: <on/off> · assumption: <what must be true to deploy safely> + +## References +- Epic: `./epic.md` · Implementation plan: `./implementation-plan-<n>.md` +- Feature design: `../../design/feature-design.md` · Coverage/contracts baseline: each target repo's `.sdd/manifest.json` diff --git a/.sdd/templates/feature-package/test-strategy/test-strategy.template.md b/.sdd/templates/feature-package/test-strategy/test-strategy.template.md new file mode 100644 index 000000000..01d07120d --- /dev/null +++ b/.sdd/templates/feature-package/test-strategy/test-strategy.template.md @@ -0,0 +1,123 @@ +<!-- ─────────────────────────────── + Template: Test Strategy + Template-ID: test-strategy + Generates: features/<KEY>/test-strategy.md + Description: Per-feature/system test plan — use-cases→tests, contract/integration/E2E/scale/security/resiliency tiers. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Test Strategy — <feature title> + +> Start here → repo root [`AGENTS.md`](../../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../ai-docs/ARCHITECTURE.md). This plan proves the spec [`feature-spec.md`](spec/feature-spec.md) + design [`feature-design.md`](design/feature-design.md). (Generated at `features/<KEY>/test-strategy.md`; links relative to that.) +> Context-efficiency: link to canonical docs — don't duplicate them; module-level unit tests live in the +> source-local module spec at `<module-path>/ai-docs/<module-name>-spec.md`. + +<!-- + Per-feature/system test plan (module-level unit tests live in the source-local module spec). Headings are flat; sections + preceded by `<!-- Include if: ... -->` are kept only when the condition holds. Each section comment gives + Capture / Avoid / Example. Name the E2E framework from the repo's REAL test setup — never assume web/JS. +--> + +## Metadata + +| Field | Value | +|---|---| +| Feature / ticket key | <KEY> | +| Feature Spec | `spec/feature-spec.md` | +| Feature Design | `design/feature-design.md` | +| Generated from | `test-strategy` @ SDLC template library `0.2.2` | + +## References +<!-- Capture: link the feature spec (acceptance, change class) + repo architecture. Avoid: a test plan with no + link to what it's proving. --> +- Feature Spec: `spec/feature-spec.md` (acceptance criteria, change class) +- Repo architecture: `../../ai-docs/ARCHITECTURE.md` + +## Test Config Variables +<!-- Capture: flag/config combinations that affect behavior under test. Avoid: testing only the default config. + Example: "<featureFlag> = on/off; <dimension> = <value-a>/<value-b>." --> +| Variable | Possible values | +|---|---| + +## Use Cases → Tests +<!-- Capture: every acceptance criterion → a test with a positive AND a negative case. Avoid: only the positive + case (the negative is what catches regressions). Example: "process <limit> items (positive) | reject >limit (negative)." --> +| # | Use case / acceptance criterion | Positive case | Negative case (must NOT fire when it shouldn't) | Status | +|---|---|---|---|---| + +<!-- Include if: the change affects cross-service contracts --> +## Contract Tests +<!-- Capture: the consumer/producer contract scenarios + CI stage. Avoid: shipping a contract change with no + contract test. Example: "<operation> schema | <consumer> | <producer> | PR CI." --> +| Scenario / interface | Consumer | Producer | CI stage | +|---|---|---|---| + +<!-- Include if: the change crosses service boundaries --> +## Integration Tests +<!-- Capture: cross-boundary scenarios + whether automated/in CI. Avoid: a manual-only integration check for a + critical path. Example: "apply operation then verify event consumed downstream | automated | yes." --> +| Scenario | Suite | Automated | In CI | +|---|---|---|---| + +<!-- Include if: the feature is user-visible / system-level --> +## System / E2E Tests +<!-- Capture: end-to-end scenarios + tags + CI stage. Avoid: E2E that duplicates unit coverage. Example: + "user completes `<operation>` flow → target state reached | @smoke | nightly." --> +| Scenario | Suite | Tags | CI stage | +|---|---|---|---| + +<!-- Include if: the change is perf-critical --> +## Scale / Load Tests +<!-- Capture: the load scenario + type + CI stage. Avoid: a perf claim with no load test. Example: "<limit>-item + batch p99 < 5s | load | pre-release." --> +| Scenario | Type | CI stage | +|---|---|---| + +<!-- Include if: the change touches a security surface --> +## Security Tests +<!-- Capture: the security test type + tracker. Avoid: skipping authz tests on a privileged op. Example: + "<operation> authorization | SAST + manual authorization test | <tracker-key>." --> +| Service | Test type (pen-test / SAST / DAST) | Tracker | +|---|---|---| + +<!-- Include if: the change affects availability / failure / recovery paths --> +## Resiliency Tests +<!-- Capture: the failure injected + expected behavior. Avoid: assuming dependencies never fail. Example: + "event bus down mid-batch | partial results returned, retried on recovery." --> +| Failure injected | Expected behavior | Suite | In CI | +|---|---|---|---| + +<!-- Include if: the feature warrants verification after deploy (smoke / synthetic / canary) --> +## Production / Post-Deploy Tests +<!-- Capture: the post-deploy check + signal + rollback trigger. Avoid: enabling for all users with no canary. + Example: "canary 5% | error-rate signal | rollback if > 1%." --> +| Check | Signal it watches | Trigger (smoke / synthetic / canary) | Rollback if | +|---|---|---|---| + +<!-- Include if: testing depends on another team/service/environment being ready --> +## QA Dependencies +<!-- Capture: each external dependency the testing needs + readiness. Avoid: discovering a missing test env + late. Example: "staging bulk-data set | for load tests | not ready." --> +| Dependency (team / service / env / data) | Needed for | Ready? | +|---|---|---| + +## E2E Framework & Location +<!-- + Capture: name the framework + where its tests live + tags + CI stage, read from the repo's REAL test setup. + Avoid: defaulting to a web/JS framework when the repo isn't web. Example (service): "integration suite at + src/test/integration/, tag @INTEGRATION, runs in the PR CI stage." +--> +- Framework: <name> · Test directory: `<path>` · Tag convention: <tags> · Runs in CI: <stage> + +## Coverage Summary +<!-- Capture: per test type, the scenario count + automated/in-CI + status. Avoid: claiming coverage with no + count. Example: "Integration | 4 | yes | yes | green." --> +| Test type | Scenarios | Automated | In CI | Status | +|---|---|---|---|---| + +## Gaps / Risks +<!-- Capture: known testing gaps + impact + mitigation. Avoid: hiding a known gap. Example: "no load test for + the eu region | risk: undetected regional latency | mitigation: add before GA." --> +| Gap | Impact | Mitigation | +|---|---|---| diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..522d6e95b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,122 @@ +<!-- ─────────────────────────────── + Template: AGENTS.md + Template-ID: agents + Generates: AGENTS.md + Description: Agent entry contract — first file every AI agent reads (commands, rules, boundaries, routing). + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# AGENTS.md — react-widgets + +> Read first. Next: [`ai-docs/SPEC_INDEX.md`](ai-docs/SPEC_INDEX.md) for routing and [`ai-docs/ARCHITECTURE.md`](ai-docs/ARCHITECTURE.md) for system shape. Load only the module specs needed for the task. + +## Repo Overview + +**react-widgets** is a browser-focused React package repository that provides Webex widgets, reusable UI components, Redux modules, host-integration helpers, build/release tooling, and browser journey tests. + +**What it is:** + +- A single root build that produces many `@webex/*` npm packages and selected CDN widget bundles. +- A client library that consumes Webex JavaScript SDK services and exposes React, browser-global, data-attribute, and event surfaces. + +**What it is NOT:** + +- It is not the Webex backend or the Webex JavaScript SDK implementation. +- It does not own a server datastore or API schema. +- It is not a native mobile or desktop application. + +## Tech Stack + +- JavaScript and TypeScript; React 16, Redux 3, Immutable.js, React-Redux, and recompose. +- Babel 7, Rollup 2, Webpack 4, npm, Jest 24, WebdriverIO 7, Sauce Labs, and axe-core. +- CI currently runs Node 22.22; `.nvmrc` selects the Node 22 LTS line (`lts/jod`). + +## Architecture + +```text +host application / HTML data attributes + ↓ +webex-widget-base (mount, auth/SDK, Redux provider, events, teardown) + ↓ +capability widgets → containers/HOCs → Redux modules → Webex JS SDK/services + ↓ ↘ shared React components +host callbacks + DOM/ampersand events +``` + +Full component responsibilities and interaction diagrams: [`ai-docs/ARCHITECTURE.md`](ai-docs/ARCHITECTURE.md). + +## Module / Package Structure + +```text +packages/node_modules/@webex/ +├── widget-* # capability widgets and demos +├── react-component-* # reusable UI components +├── react-container-* # state/SDK-connected components +├── react-hoc-* # reusable higher-order behavior +├── redux-module-* # actions, reducers, constants, helpers, and/or thunks; view selectors live in widget/container packages +├── react-redux-* # SDK/store integration, fixtures, metrics +└── webex-widget-base/ # common host/runtime composition +scripts/ # build, start, publish, deploy, and tooling commands +test/journeys/ # browser integration and accessibility suites +``` + +Module routing: [`ai-docs/SPEC_INDEX.md`](ai-docs/SPEC_INDEX.md). + +## Critical Rules + +1. **Code and tests describe current behavior.** Never invent a package export, prop, event, command, destination type, or SDK capability. +2. **Plan and confirm behavior changes.** This bootstrap is docs-only; future public API, event, auth, build, release, or test-policy changes require an approved spec delta. +3. **Preserve package entrypoints.** `src/index.js` / `src/index.ts`, package `main`/`module`, widget browser names, data-toggle names, and event strings are compatibility surfaces. +4. **Preserve enhancer ordering.** Data API and browser globals wrap Redux setup, widget removal, SDK injection, current-user loading, display name, and version metadata in `webex-widget-base`. +5. **Keep Redux state immutable.** Follow existing action/reducer/thunk patterns and never mutate stored records in place. +6. **Treat tokens and SDK instances as host-supplied credentials.** Never commit or log access tokens, guest tokens, client secrets, Sauce keys, npm tokens, Netlify tokens, or AWS credentials. +7. **Keep accessibility behavior testable.** Maintain roles, labels, keyboard navigation, focus transitions, and axe journey coverage when changing UI. +8. **Update SDD docs with behavior.** Change the owning module spec, contracts/state indexes, and manifest in the same change. + +## Essential Commands + +| Role | Command | +|---|---| +| Install | `npm install --legacy-peer-deps` | +| Build | `npm run build:all` | +| Unit test | `npm run jest` | +| Lint/format | `npm run static-analysis` | + +→ Full command surface, toolchain, and registries: **[GETTING_STARTED.md](./ai-docs/GETTING_STARTED.md)** · test tiers and coverage gate: **[TEST_INDEX.md](./ai-docs/TEST_INDEX.md)** + +## Common Gotchas + +1. Package source is intentionally tracked under `packages/node_modules/@webex/`; do not treat it as installed third-party output. +2. Jest remaps `@webex/*` imports to package `src/` entrypoints, so tests exercise source rather than built `cjs/` or `es/` output. +3. Space and Recents use encrypted Mercury flows by default; Recents `basicMode` uses Webex REST and changes encryption behavior. +4. Widget teardown must call the browser widget's `remove()` path so React unmounts and `window.webex.widgetStore` is cleared. +5. The protected legacy docs include `@ciscospark/*` rename notices; keep them, but use current `@webex/*` entrypoints for new work. +6. Browser journey suites need credentials, built static assets, media permissions, and sometimes Sauce Connect; Jest does not cover those integration requirements. + +## Pre-Commit Checklist + +- [ ] `npm run static-analysis` passes. +- [ ] `npm run jest` passes. +- [ ] Positive and negative behavior tests cover changed logic; relevant journey/accessibility tests are identified. +- [ ] Public exports, props, events, browser/data APIs, and package compatibility remain intentional. +- [ ] Spec/docs and `.sdd/manifest.json` are current; local SDD run output remains ignored and unstaged. +- [ ] No credentials, generated bundles, `es/`, `cjs/`, `dist/`, reports, or coverage output are committed unintentionally. + +## External Source Access + +| Provider class | Source / host pattern | Preferred access | If unavailable | +|---|---|---|---| +| source host | `github.com/webex/*`, Cisco enterprise GitHub | authenticated CLI/connector or local clone | Use local code/history; do not guess missing PR rationale. | +| Webex APIs/SDK docs | `developer.webex.com`, `github.com/webex/webex-js-sdk` | official docs or installed dependency source | Mark contract detail unverified until access is available. | +| CI/browser grid | CircleCI, Sauce Labs | CI artifacts and authenticated service | Run local Jest/static analysis and record the journey gap. | + +## Strict Compliance Mode (automation) + +For SDD bootstrap, validation, CI, and release work, load the affected specs up front. A Blocking source-fidelity, conformance, code/spec, security, or public-contract finding halts progression until resolved. + +--- + +Per-module coverage state lives in `.sdd/manifest.json` and is mirrored in `ai-docs/SPEC_INDEX.md`. Independent spec-validator **Pass** at `548a0312` (validator `codex-desktop`); see [`ai-docs/SDD_BOOTSTRAP_EVIDENCE.md`](ai-docs/SDD_BOOTSTRAP_EVIDENCE.md). Specs remain `Partial` because of documented weak-evidence gaps and the promotion-history gate. + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-09-03`. diff --git a/ai-docs/ARCHITECTURE.md b/ai-docs/ARCHITECTURE.md new file mode 100644 index 000000000..7b5d0ed8d --- /dev/null +++ b/ai-docs/ARCHITECTURE.md @@ -0,0 +1,221 @@ +<!-- ─────────────────────────────── + Template: ARCHITECTURE + Template-ID: architecture + Generates: ai-docs/ARCHITECTURE.md + Description: Repo/component architecture — components, responsibilities, interactions, cross-cutting posture. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# ARCHITECTURE — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), then route through [`SPEC_INDEX.md`](SPEC_INDEX.md). Module detail lives under `ai-docs/modules/`. + +## Design Overview + +The repository is one build and release unit containing a package forest. Small presentational packages remain independently importable, while widgets compose them with connected containers, Redux modules, Webex SDK adapters, and a common browser runtime. This separation lets consumers use either a complete widget or lower-level pieces without duplicating Webex-specific state and integration behavior. + +`webex-widget-base` is the host boundary for legacy JavaScript widgets. Its enhancer chain registers the data API and browser globals, creates a Redux provider, injects Webex SDK state/authentication, loads the current user, exposes version metadata, and supports teardown. Newer TypeScript calling widgets instead receive component adapters through React contexts and expose typed React entrypoints. Evidence: `packages/node_modules/@webex/webex-widget-base/src/index.js`, `packages/node_modules/@webex/widget-call-history/src/contexts/AdapterContext.tsx`. + +## Component Inventory & Responsibilities + +| Component | Responsibility | Docs | +|---|---|---| +| Space and messaging packages | Compose space activities, messaging, files, roster, destinations, and host events. | `modules/space-messaging-spec.md` | +| Recents package | Load/filter spaces, react to realtime activities and membership changes, and emit host selection/call/profile events. | `modules/recents-spec.md` | +| Meetings packages | Resolve destinations and manage create/join/media/leave state and UI. | `modules/meetings-spec.md` | +| Calling packages | Render call history, dial pad, speed dials, voicemail, and adapter-driven call actions. | `modules/calling-spec.md` | +| Shared UI components | Provide presentational building blocks and UI utilities. | `modules/shared-ui-components-spec.md` | +| Redux/state packages | Own immutable client state, SDK thunks, event reducers, and metrics queues. View selectors live in widget/container packages. | `modules/state-management-spec.md` | +| Containers/HOCs | Bind UI to Redux, SDK operations, file retrieval, notifications, presence, scrolling, and Mercury. | `modules/containers-hooks-spec.md` | +| Widget runtime/auth/demos | Mount widgets, establish auth/SDK context, expose browser/data APIs, and provide demos/samples. | `modules/widget-runtime-auth-spec.md` | +| Build/release tooling | Discover packages and build, transpile, sign, publish, deploy, and serve artifacts. | `modules/build-release-tooling-spec.md` | +| Test automation | Run Jest and browser journeys, accessibility checks, test-user setup, and CI reporting. | `modules/test-automation-spec.md` | + +## Component Interaction + +```mermaid +flowchart LR + Host[Host application or HTML] --> Runtime[Widget runtime and auth] + Runtime --> Widgets[Space, Recents, Meetings, Calling widgets] + Widgets --> Containers[Containers and HOCs] + Widgets --> UI[Shared UI components] + Containers --> State[Redux and state packages] + State --> SDK[Webex JavaScript SDK and component adapters] + SDK --> Services[Webex API and realtime services] + Widgets --> Events[Host callbacks, DOM events, ampersand events] + Tooling[Build and release tooling] --> Packages[ES, CommonJS, and CDN artifacts] + Tests[Test automation] --> Host + Tests --> Widgets +``` + +Hosts enter through imported React exports, `window.webex.widget(element)`, or `[data-toggle^="webex-"]`. Legacy widgets compose shared enhancers and reducers; Redux thunks call SDK plugins, and realtime listeners feed normalized state and host events. Calling widgets use typed adapter contexts rather than the legacy shared Redux runtime. + +## Widget Initialization & Interaction Flow + +```mermaid +sequenceDiagram + participant H as Host + participant B as webex-widget-base + participant R as Redux store + participant S as Webex SDK + participant W as Capability widget + H->>B: import/render or browser/data API options + B->>R: create reducers + thunk middleware + B->>S: inject supplied SDK or authenticate supplied token + S-->>R: instance and registration state + R-->>W: selectors inject current state + W->>S: thunk or adapter operation + S-->>W: response or realtime event + W-->>H: callback and/or DOM event + alt host removes widget + H->>B: remove() + B->>H: unmount React and remove widget-store entry + Note over B,R: browser remove() does not dispatch REMOVE_WIDGET + end +``` + +Evidence: `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js`, `packages/node_modules/@webex/widget-recents/src/enhancers/listeners.js`. + +## Dependencies + +| Dependency | Type | How used | Failure / version handling | +|---|---|---|---| +| React / ReactDOM | peer/runtime | Render components and mount browser widgets. | Root dependency range is `^16.8.4`; public component behavior is semver-sensitive. | +| Redux / React-Redux / Immutable.js | internal runtime | Widget stores, actions, reducers, selectors, and connected containers. | Reducers retain initial state on unknown actions; failed SDK operations enter error state or reject thunks. | +| Webex JavaScript SDK plugins | external packages/services | Authentication, devices, conversations, rooms, people, meetings, Mercury, search, teams, flags, and metrics. | Versions are pinned to the compatible `^2.60.4` family; callers surface errors/loading state. | +| Momentum UI and Webex Components | external package | Visual primitives, collaboration controls, and calling adapters. | Root versions are pinned; CSS and host-theme compatibility must be checked on upgrades. | +| Browser APIs | host platform | DOM mount, CustomEvent, media streams, notifications, localStorage, and audio/video. | Browser support and test configuration are explicit; unavailable permissions surface errors or disable behavior. | +| Babel/Rollup/Webpack | build | Transpile packages and bundle widgets. | Build fails on compile/config errors; generated `es/`, `cjs/`, and `dist/` are not source. | + +### State Model + +- Each legacy widget creates a combined Redux store with widget reducers plus `spark` and `users`. The reducer resets if `REMOVE_WIDGET` is dispatched, but the browser `remove()` implementation does not dispatch it. Evidence: `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`. +- State packages use Immutable.js maps/records for activities, conversations, spaces, users, media, meetings, errors, features, flags, presence, search, teams, and metrics. Evidence: `packages/node_modules/@webex/redux-module-meetings/src/reducer.js`. +- Setup enhancers advance from SDK authentication/registration to Mercury connection, initial fetch, avatar/team/feature loading, ready/error display, and teardown. Evidence: `packages/node_modules/@webex/widget-recents/src/enhancers/setup.js`. + +## Cross-Cutting Concerns + +- **Security:** credentials enter as host props or environment variables; SDK/API calls cross the trust boundary. Never persist or log tokens. Browser globals and data attributes are public inputs and must be validated against component prop contracts. +- **Observability:** development Redux logging, SDK logger calls, CI JUnit/browser artifacts, and the metrics HOC expose state/action timing and failures. Call payloads are deliberately omitted from Recents event logs to avoid range/serialization problems. + +## Performance, Compatibility & Accessibility + +The repository targets the browser matrix in `babel.config.js`, including legacy ES5 output. Rollup externalizes core peer UI libraries and hashes CSS-module class names; Webpack produces self-contained widget bundles. Recents loads a bounded initial set (default 25), then avatars/teams/features, and accessibility journeys run axe checks. Public package exports, widget names, data attributes, event strings, keyboard behavior, and SRI manifests are compatibility surfaces. + +## Dependency / Interaction Topology + +```mermaid +flowchart TB + Widgets --> Base[webex-widget-base] + Widgets --> Containers + Widgets --> Components + Containers --> Redux[redux-module-*] + Base --> Spark[react-redux-spark] + Redux --> SDK[Webex SDK plugins] + Spark --> SDK + SDK -. realtime .-> Mercury[Mercury] + Mercury -. activities .-> Redux + Widgets -. events .-> Host +``` + +| From | To | Kind | Purpose | +|---|---|---|---| +| Widget entrypoints | `webex-widget-base` | in-process composition | Install common host, Redux, auth, version, and teardown behavior. | +| Containers/HOCs | Redux modules | in-process calls | Dispatch state transitions and select display props. | +| Redux thunks/runtime | Webex SDK | promise/event calls | Fetch or mutate Webex resources and connect realtime services. | +| Widgets | Host | callbacks/DOM events | Report message, room, call, membership, profile, and activity behavior. | + +## Object / Data Ownership + +| Domain object | Client-state owner | Read by | +|---|---|---| +| SDK authentication/registration state | `react-redux-spark` | all legacy widgets and setup enhancers | +| Space/conversation/activity/user/team state | matching `redux-module-*` packages | Space, Recents, Message, Roster, and connected components | +| Call/media state | `redux-module-media` and widget reducers | Meet/Space/Recents call UI | +| Meeting IDs and media readiness | `redux-module-meetings` | Meetings widget selectors/components | +| Widget-local status/config | each widget reducer or React hook state | owning widget only | + +Remote Webex services remain systems of record; this repository owns only client representations and transitions. + +## Caching Catalog + +| Cache | Backend | What it holds | Lifetime | Invalidation trigger | +|---|---|---|---|---| +| widget store | `window.webex.widgetStore` | mounted `BrowserWidget` objects by UUID | widget lifetime | `remove()` deletes the UUID entry | +| Redux resource maps | in-memory Immutable.js store | spaces, users, activities, files, meetings, and related status | widget/Provider lifetime | actions replace/remove records; an explicit `REMOVE_WIDGET` action resets state, but browser `remove()` does not dispatch it | +| downloaded share reuse | Redux share state | decrypted/downloadable file blob | widget/Provider lifetime | new fetch/reducer update; browser removal relies on React unmount and reference release | +| number-pad focus flags | browser `localStorage` | focus handoff flags | interaction-scoped | blur/unmount handlers remove keys | + +## Observability Patterns + +- **Logging:** SDK logger calls record setup, events, warnings, and adaptive-card failures; development stores add `redux-logger`. Do not log tokens or raw call objects. +- **Metrics:** `react-redux-spark-metrics` queues named start/end metrics and sends them through the SDK metrics plugin when available. +- **Audit:** CI stores JUnit and browser artifacts; no product audit trail is owned by this client library. + +## Infrastructure Matrix + +| Category | In use | Notes | +|---|---|---| +| Datastores | none owned | Browser/Redux state only; Webex services own remote data. | +| Messaging / streaming | Webex Mercury | SDK-managed realtime conversation/activity channel. | +| Cloud / platform services | CircleCI, Sauce Labs, AWS S3/CloudFront, npm registry, Netlify tooling | Build, browser test, publish, and CDN deployment integrations. | + +## Shared / Base Libraries + +| Library | Shared responsibility | Version floor/source | +|---|---|---| +| `@webex/webex-widget-base` | Legacy widget mount, Redux, SDK/auth, current user, browser/data APIs, teardown, intl composition. | `packages/node_modules/@webex/webex-widget-base/src/index.js` | +| `@webex/react-component-utils` | Hydra IDs, activity/file/card/string/mention helpers and constants. | `packages/node_modules/@webex/react-component-utils/src/index.js` | +| `@webex/react-redux-spark` | SDK instance/authentication/device state injection. | `packages/node_modules/@webex/react-redux-spark/src/index.js` | +| Momentum UI / Webex Components | Shared visual controls and adapter interfaces. | pinned in `package.json` | + +## Package Map & Inter-Package Dependencies + +- Workspace convention: tracked packages live under `packages/node_modules/@webex/*`; a root discovery utility enumerates package paths rather than npm workspaces. +- Visibility: packages with `private: true` are build/demo/internal-only; other package entrypoints are publishable npm surfaces. +- Dependency direction: widgets depend on base/runtime, containers, Redux modules, and components; containers depend on Redux/SDK helpers; components should remain reusable and primarily presentational. +- Release rule: root versioning and publish tooling build non-private packages together; breaking exported changes require a compatible release and migration note. + +## Release & Versioning + +- `standard-version` generates root versions/changelog entries using Conventional Commits. Non-private packages are built and published publicly through root tooling. +- CI builds Space, Recents, and Demo CDN archives, creates SRI manifests/signatures, syncs alpha/latest/archive prefixes to S3, and invalidates CloudFront. +- Consumers can read bundle version metadata from headers, `window.webex.widgetFn.{name}.version`, or a mounted widget object. + +## Host Integration & Theming + +- Imported React components use package entrypoints and package CSS/Sass as documented by the owning module. +- Browser-global hosts call `window.webex.widget(element).{name}Widget(options)`; data-API hosts use `data-toggle="webex-{name}"` plus data attributes. +- Hosts provide tokens or a pre-authenticated SDK instance and must load compatible Webex SDK/plugin versions. The legacy alias `window.ciscospark` points at `window.webex`. +- Momentum UI/Webex Components styles and generated CSS-module class names are host-facing visual dependencies. + +## Cross-Repo Dependency Graph + +- **Internal packages consumed:** Webex JS SDK plugins, Webex Components, adapter interfaces, Webex style guide, and test-helper packages. +- **Consumers:** npm applications and sites embedding CDN bundles or importing individual `@webex/*` packages. +- **External services:** Webex identity/API/realtime services, npm, Sauce Labs, AWS S3/CloudFront, CircleCI, and optional Netlify deployment. + +## Security Architecture + +The browser host is outside the library trust boundary and supplies credentials, DOM elements, data attributes, callback functions, destination IDs, and optional SDK adapters. `react-redux-spark` establishes authenticated SDK state, after which setup enhancers register devices/connect Mercury and dispatch SDK operations. Network transport and remote authorization are owned by the Webex SDK/services; this repository must avoid broadening token exposure through logs, browser storage, examples, or generated artifacts. + +--- + +Per-module orientation and design: [`SPEC_INDEX.md`](SPEC_INDEX.md). + +## Architecture Reference Links + +| Reference | Location | When to read | +|---|---|---| +| Architecture decisions | `adr/` | Before changing module boundaries, central doc layout, or compatibility policy. | +| Repo patterns | `patterns/` | Before adding components, Redux behavior, or package exports. | +| Enforceable rules | `RULES.md` and `rules/` | Before any implementation or release change. | + +## WS6 References + +No WS6/platform architecture document is tracked in this repository. External architecture claims are unsupported until an authoritative source is supplied; current local implications derive only from `package.json`, source, tests, and CI configuration. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/CONTRACTS.md b/ai-docs/CONTRACTS.md new file mode 100644 index 000000000..ef809f99e --- /dev/null +++ b/ai-docs/CONTRACTS.md @@ -0,0 +1,213 @@ +<!-- ─────────────────────────────── + Template: Contracts Catalog + Template-ID: contracts + Generates: ai-docs/CONTRACTS.md + Description: Standing as-built public-surface catalog (Provides/Requires) + compatibility policy. + Library ver: 0.2.2 + Last updated: 2026-07-11 +─────────────────────────────── --> + +# Contracts Catalog — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), route through [`SPEC_INDEX.md`](SPEC_INDEX.md), and use this as the root public-surface index. Exact declarations live at package entrypoints; `.sdd/manifest.json` carries the machine baseline. + +> Read before adding or changing a package export, widget host API, event, or root command. This catalog summarizes entrypoints and links to source; it does not duplicate full prop/type declarations. + +### Exported API & Types + +| Contract ID | Owner module | Symbol / package surface | Signature | Stability / deprecation | Detail and definition | +|---|---|---|---|---|---| +| `rw.ui.activity-item` | shared-ui-components | `@webex/react-component-activity-item` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-item/src/index.js` | +| `rw.ui.activity-item-base` | shared-ui-components | `@webex/react-component-activity-item-base` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-item-base/src/index.js` | +| `rw.ui.activity-list` | shared-ui-components | `@webex/react-component-activity-list` | default component + item-type constants | public semver surface | `packages/node_modules/@webex/react-component-activity-list/src/index.js` | +| `rw.ui.activity-menu` | shared-ui-components | `@webex/react-component-activity-menu` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-menu/src/index.js` | +| `rw.ui.activity-menu-header` | shared-ui-components | `@webex/react-component-activity-menu-header` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-menu-header/src/index.js` | +| `rw.ui.activity-post` | shared-ui-components | `@webex/react-component-activity-post` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-post/src/index.js` | +| `rw.ui.activity-post-action` | shared-ui-components | `@webex/react-component-activity-post-action` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-post-action/src/index.js` | +| `rw.ui.activity-share-file` | shared-ui-components | `@webex/react-component-activity-share-file` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-share-file/src/index.js` | +| `rw.ui.activity-share-files` | shared-ui-components | `@webex/react-component-activity-share-files` | connected default + named component | public semver surface | `packages/node_modules/@webex/react-component-activity-share-files/src/index.js` | +| `rw.ui.activity-share-thumbnail` | shared-ui-components | `@webex/react-component-activity-share-thumbnail` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-share-thumbnail/src/index.js` | +| `rw.ui.activity-system-message` | shared-ui-components | `@webex/react-component-activity-system-message` | component + system-message verb constants | public semver surface | `packages/node_modules/@webex/react-component-activity-system-message/src/index.js` | +| `rw.ui.activity-text` | shared-ui-components | `@webex/react-component-activity-text` | default React component | public semver surface | `packages/node_modules/@webex/react-component-activity-text/src/index.js` | +| `rw.ui.adaptive-card` | shared-ui-components | `@webex/react-component-adaptive-card` | connected React component | public semver surface | `packages/node_modules/@webex/react-component-adaptive-card/src/index.js` | +| `rw.ui.add-file-button` | shared-ui-components | `@webex/react-component-add-file-button` | default React component | public semver surface | `packages/node_modules/@webex/react-component-add-file-button/src/index.js` | +| `rw.ui.audio` | shared-ui-components | `@webex/react-component-audio` | media-stream React component | public semver surface | `packages/node_modules/@webex/react-component-audio/src/index.js` | +| `rw.ui.avatar` | shared-ui-components | `@webex/react-component-avatar` | default React component | public semver surface | `packages/node_modules/@webex/react-component-avatar/src/index.js` | +| `rw.ui.badge` | shared-ui-components | `@webex/react-component-badge` | default React component | public semver surface | `packages/node_modules/@webex/react-component-badge/src/index.js` | +| `rw.ui.button` | shared-ui-components | `@webex/react-component-button` | default React component | public semver surface | `packages/node_modules/@webex/react-component-button/src/index.js` | +| `rw.ui.button-controls` | shared-ui-components | `@webex/react-component-button-controls` | default React component | public semver surface | `packages/node_modules/@webex/react-component-button-controls/src/index.js` | +| `rw.ui.call-data-activity` | shared-ui-components | `@webex/react-component-call-data-activity` | default call activity component | public semver surface | `packages/node_modules/@webex/react-component-call-data-activity/src/index.js` | +| `rw.ui.chip-base` | shared-ui-components | `@webex/react-component-chip-base` | default React component | public semver surface | `packages/node_modules/@webex/react-component-chip-base/src/index.js` | +| `rw.ui.chip-file` | shared-ui-components | `@webex/react-component-chip-file` | default React component | public semver surface | `packages/node_modules/@webex/react-component-chip-file/src/index.js` | +| `rw.ui.confirmation-modal` | shared-ui-components | `@webex/react-component-confirmation-modal` | default modal component | public semver surface | `packages/node_modules/@webex/react-component-confirmation-modal/src/index.js` | +| `rw.ui.cover` | shared-ui-components | `@webex/react-component-cover` | default React component | public semver surface | `packages/node_modules/@webex/react-component-cover/src/index.js` | +| `rw.ui.day-separator` | shared-ui-components | `@webex/react-component-day-separator` | default React component | public semver surface | `packages/node_modules/@webex/react-component-day-separator/src/index.js` | +| `rw.ui.error-display` | shared-ui-components | `@webex/react-component-error-display` | default error component | public semver surface | `packages/node_modules/@webex/react-component-error-display/src/index.js` | +| `rw.ui.file-share-display` | shared-ui-components | `@webex/react-component-file-share-display` | default React component | public semver surface | `packages/node_modules/@webex/react-component-file-share-display/src/index.js` | +| `rw.ui.file-staging-area` | shared-ui-components | `@webex/react-component-file-staging-area` | default React component | public semver surface | `packages/node_modules/@webex/react-component-file-staging-area/src/index.js` | +| `rw.ui.icon` | shared-ui-components | `@webex/react-component-icon` | default icon + `ICONS` catalog | public semver surface | `packages/node_modules/@webex/react-component-icon/src/index.js` | +| `rw.ui.incoming-call` | shared-ui-components | `@webex/react-component-incoming-call` | default call component | public semver surface | `packages/node_modules/@webex/react-component-incoming-call/src/index.js` | +| `rw.ui.join-call-button` | shared-ui-components | `@webex/react-component-join-call-button` | default React component | public semver surface | `packages/node_modules/@webex/react-component-join-call-button/src/index.js` | +| `rw.ui.list-separator` | shared-ui-components | `@webex/react-component-list-separator` | default React component | public semver surface | `packages/node_modules/@webex/react-component-list-separator/src/index.js` | +| `rw.ui.loading-screen` | shared-ui-components | `@webex/react-component-loading-screen` | default React component | public semver surface | `packages/node_modules/@webex/react-component-loading-screen/src/index.js` | +| `rw.ui.md-choiceset-input` | shared-ui-components | `@webex/react-component-md-choiceset-input` | adaptive-card input component | public semver surface | `packages/node_modules/@webex/react-component-md-choiceset-input/src/index.js` | +| `rw.ui.md-text-input` | shared-ui-components | `@webex/react-component-md-text-input` | adaptive-card input component | public semver surface | `packages/node_modules/@webex/react-component-md-text-input/src/index.js` | +| `rw.ui.md-toggle-input` | shared-ui-components | `@webex/react-component-md-toggle-input` | adaptive-card input component | public semver surface | `packages/node_modules/@webex/react-component-md-toggle-input/src/index.js` | +| `rw.ui.new-messages-separator` | shared-ui-components | `@webex/react-component-new-messages-separator` | default React component | public semver surface | `packages/node_modules/@webex/react-component-new-messages-separator/src/index.js` | +| `rw.ui.people-list` | shared-ui-components | `@webex/react-component-people-list` | default React component | public semver surface | `packages/node_modules/@webex/react-component-people-list/src/index.js` | +| `rw.ui.presence-avatar` | shared-ui-components | `@webex/react-component-presence-avatar` | default React component | public semver surface | `packages/node_modules/@webex/react-component-presence-avatar/src/index.js` | +| `rw.ui.ringtone` | shared-ui-components | `@webex/react-component-ringtone` | component + incoming/ringback constants | public semver surface | `packages/node_modules/@webex/react-component-ringtone/src/index.js` | +| `rw.ui.scroll-to-bottom` | shared-ui-components | `@webex/react-component-scroll-to-bottom-button` | default React component | public semver surface | `packages/node_modules/@webex/react-component-scroll-to-bottom-button/src/index.js` | +| `rw.ui.space-item` | shared-ui-components | `@webex/react-component-space-item` | default React component | public semver surface | `packages/node_modules/@webex/react-component-space-item/src/index.js` | +| `rw.ui.spaces-list` | shared-ui-components | `@webex/react-component-spaces-list` | default React component | public semver surface | `packages/node_modules/@webex/react-component-spaces-list/src/index.js` | +| `rw.ui.spark-fonts` | shared-ui-components | `@webex/react-component-spark-fonts` | font stylesheet side-effect entry | public semver surface | `packages/node_modules/@webex/react-component-spark-fonts/src/index.js` | +| `rw.ui.spark-logo` | shared-ui-components | `@webex/react-component-spark-logo` | default logo component | public semver surface | `packages/node_modules/@webex/react-component-spark-logo/src/index.js` | +| `rw.ui.spark-oauth` | shared-ui-components | `@webex/react-component-spark-oauth` | default OAuth UI component | public semver surface; legacy name | `packages/node_modules/@webex/react-component-spark-oauth/src/index.js` | +| `rw.ui.spinner` | shared-ui-components | `@webex/react-component-spinner` | default React component | public semver surface | `packages/node_modules/@webex/react-component-spinner/src/index.js` | +| `rw.ui.textarea` | shared-ui-components | `@webex/react-component-textarea` | default React component | public semver surface | `packages/node_modules/@webex/react-component-textarea/src/index.js` | +| `rw.ui.timer` | shared-ui-components | `@webex/react-component-timer` | default React component | public semver surface | `packages/node_modules/@webex/react-component-timer/src/index.js` | +| `rw.ui.title-bar` | shared-ui-components | `@webex/react-component-title-bar` | default React component | public semver surface | `packages/node_modules/@webex/react-component-title-bar/src/index.js` | +| `rw.ui.typing-avatar` | shared-ui-components | `@webex/react-component-typing-avatar` | default React component | public semver surface | `packages/node_modules/@webex/react-component-typing-avatar/src/index.js` | +| `rw.ui.typing-indicator` | shared-ui-components | `@webex/react-component-typing-indicator` | default React component | public semver surface | `packages/node_modules/@webex/react-component-typing-indicator/src/index.js` | +| `rw.ui.utils` | shared-ui-components | `@webex/react-component-utils` | named utility/constants barrel | public semver surface | `packages/node_modules/@webex/react-component-utils/src/index.js` | +| `rw.ui.video` | shared-ui-components | `@webex/react-component-video` | media-stream React component | public semver surface | `packages/node_modules/@webex/react-component-video/src/index.js` | +| `rw.container.activity-list` | containers-hooks | `@webex/react-container-activity-list` | connected default component | public semver surface | `packages/node_modules/@webex/react-container-activity-list/src/index.js` | +| `rw.container.file-downloader` | containers-hooks | `@webex/react-container-file-downloader` | default injecting HOC | public semver surface | `packages/node_modules/@webex/react-container-file-downloader/src/index.js` | +| `rw.container.message-composer` | containers-hooks | `@webex/react-container-message-composer` | component/actions/reducer barrel | public semver surface | `packages/node_modules/@webex/react-container-message-composer/src/index.js` | +| `rw.container.notifications` | containers-hooks | `@webex/react-container-notifications` | component/actions/reducer barrel | public semver surface | `packages/node_modules/@webex/react-container-notifications/src/index.js` | +| `rw.container.presence-avatar` | containers-hooks | `@webex/react-container-presence-avatar` | connected component + reducers | public semver surface | `packages/node_modules/@webex/react-container-presence-avatar/src/index.js` | +| `rw.container.read-receipts` | containers-hooks | `@webex/react-container-read-receipts` | named component + connected default | public semver surface | `packages/node_modules/@webex/react-container-read-receipts/src/index.js` | +| `rw.container.scrolling-activity` | containers-hooks | `@webex/react-container-scrolling-activity` | named component + wrapped default | public semver surface | `packages/node_modules/@webex/react-container-scrolling-activity/src/index.js` | +| `rw.hoc.conversation-mercury` | containers-hooks | `@webex/react-hoc-conversation-mercury` | default HOC factory | public semver surface | `packages/node_modules/@webex/react-hoc-conversation-mercury/src/index.js` | +| `rw.hoc.scrollable` | containers-hooks | `@webex/react-hoc-scrollable` | default HOC factory | public semver surface | `packages/node_modules/@webex/react-hoc-scrollable/src/index.js` | +| `rw.state.spark` | state-management | `@webex/react-redux-spark` | reducer/initial state + SDK injection HOC | public semver surface | `packages/node_modules/@webex/react-redux-spark/src/index.js` | +| `rw.state.spark-fixtures` | test-automation | `@webex/react-redux-spark-fixtures` | mock store/SDK fixtures | public test-helper surface | `packages/node_modules/@webex/react-redux-spark-fixtures/src/index.js` | +| `rw.state.spark-metrics` | state-management | `@webex/react-redux-spark-metrics` | reducer/events/metrics HOC | public semver surface | `packages/node_modules/@webex/react-redux-spark-metrics/src/index.js` | +| `rw.state.activities` | state-management | `@webex/redux-module-activities` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-activities/src/index.js` | +| `rw.state.activity` | state-management | `@webex/redux-module-activity` | actions, reducer, message helper | public semver surface | `packages/node_modules/@webex/redux-module-activity/src/index.js` | +| `rw.state.avatar` | state-management | `@webex/redux-module-avatar` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-avatar/src/index.js` | +| `rw.state.conversation` | state-management | `@webex/redux-module-conversation` | actions, utilities, reducer | public semver surface | `packages/node_modules/@webex/redux-module-conversation/src/index.js` | +| `rw.state.errors` | state-management | `@webex/redux-module-errors` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-errors/src/index.js` | +| `rw.state.features` | state-management | `@webex/redux-module-features` | reducer, constants, async feature getter | public semver surface | `packages/node_modules/@webex/redux-module-features/src/index.js` | +| `rw.state.flags` | state-management | `@webex/redux-module-flags` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-flags/src/index.js` | +| `rw.state.indicators` | state-management | `@webex/redux-module-indicators` | reducer, constants, typing actions | public semver surface | `packages/node_modules/@webex/redux-module-indicators/src/index.js` | +| `rw.state.media` | state-management | `@webex/redux-module-media` | actions, enhancer, reducer, call helpers | public semver surface | `packages/node_modules/@webex/redux-module-media/src/index.js` | +| `rw.state.meetings` | meetings | `@webex/redux-module-meetings` | meeting thunks, reducer, destination lookup | public semver surface | `packages/node_modules/@webex/redux-module-meetings/src/index.js` | +| `rw.state.mercury` | state-management | `@webex/redux-module-mercury` | reducer, enhancer, realtime actions | public semver surface | `packages/node_modules/@webex/redux-module-mercury/src/index.js` | +| `rw.state.presence` | state-management | `@webex/redux-module-presence` | reducer/actions + presence constants | public semver surface | `packages/node_modules/@webex/redux-module-presence/src/index.js` | +| `rw.state.search` | state-management | `@webex/redux-module-search` | reducer/constants + user search thunk | public semver surface | `packages/node_modules/@webex/redux-module-search/src/index.js` | +| `rw.state.share` | state-management | `@webex/redux-module-share` | reducer/constants + shared-file thunk | public semver surface | `packages/node_modules/@webex/redux-module-share/src/index.js` | +| `rw.state.spaces` | state-management | `@webex/redux-module-spaces` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-spaces/src/index.js` | +| `rw.state.teams` | state-management | `@webex/redux-module-teams` | actions + reducer/initial state | public semver surface | `packages/node_modules/@webex/redux-module-teams/src/index.js` | +| `rw.state.users` | state-management | `@webex/redux-module-users` | actions, reducer, records | public semver surface | `packages/node_modules/@webex/redux-module-users/src/index.js` | +| `rw.test.react-utils` | test-automation | `@webex/react-test-utils` | intl-aware render helpers | public test-helper surface | `packages/node_modules/@webex/react-test-utils/src/index.js` | +| `rw.runtime.sign-in` | widget-runtime-auth | `@webex/webex-sign-in-page` | named typed sign-in component | public semver surface | `packages/node_modules/@webex/webex-sign-in-page/src/index.ts` | +| `rw.runtime.base` | widget-runtime-auth | `@webex/webex-widget-base` | `constructWebexEnhancer`, default helper, selected HOCs | public semver surface; default-helper discrepancy documented | `packages/node_modules/@webex/webex-widget-base/src/index.js` | +| `rw.widget.call-history` | calling | `@webex/widget-call-history` | `CallHistoryItem`, `CallHistory`, `NoHistory`, item types | public semver surface | `packages/node_modules/@webex/widget-call-history/src/index.ts` | +| `rw.widget.files` | space-messaging | `@webex/widget-files` | default connected file widget | public semver surface | `packages/node_modules/@webex/widget-files/src/index.js` | +| `rw.widget.meet` | meetings | `@webex/widget-meet` | default widget + reducers + destination types | public semver surface | `packages/node_modules/@webex/widget-meet/src/index.js` | +| `rw.widget.meetings` | meetings | `@webex/widget-meetings` | default widget + reducers + destination types | public semver surface | `packages/node_modules/@webex/widget-meetings/src/index.js` | +| `rw.widget.message` | space-messaging | `@webex/widget-message` | default widget + reducers + destination types | public semver surface | `packages/node_modules/@webex/widget-message/src/index.js` | +| `rw.widget.number-pad` | calling | `@webex/widget-number-pad` | number pad, call buttons, contact/search/popover components | public semver surface | `packages/node_modules/@webex/widget-number-pad/src/index.ts` | +| `rw.widget.recents` | recents | `@webex/widget-recents` | default widget + reducers | public semver surface | `packages/node_modules/@webex/widget-recents/src/index.js` | +| `rw.widget.roster` | space-messaging | `@webex/widget-roster` | default connected roster widget + reducers | public semver surface | `packages/node_modules/@webex/widget-roster/src/index.js` | +| `rw.widget.space` | space-messaging | `@webex/widget-space` | default widget + events/reducers/destination types | public semver surface | `packages/node_modules/@webex/widget-space/src/index.js` | +| `rw.widget.speed-dial` | calling | `@webex/widget-speed-dial` | `SpeedDials`, `SpeedDialItem`, `SpeedDialForm`, `SpeedDialSearch`, `SpeedDialAddBanner`, and exported form/list types | public semver surface | `packages/node_modules/@webex/widget-speed-dial/src/index.ts` | +| `rw.widget.voice-mail` | calling | `@webex/widget-voice-mail` | voicemail item/playback/scrubbing components | public semver surface | `packages/node_modules/@webex/widget-voice-mail/src/index.ts` | + +Internal-only package entrypoints (`private-react-component-*` and `widget-*-demo`) are indexed in their module specs but are not public npm contracts. + +Capability-level aggregate IDs provide stable cross-references from module specs; the package/event rows above and below remain the exact symbol-level catalog. + +| Contract ID | Owner module | Aggregate surface | Compatibility | Definition / exact members | +|---|---|---|---|---| +| `rw.ui.components` | shared-ui-components | 54 public `@webex/react-component-*` entrypoints | each entrypoint is a public semver surface | individual exact paths in the `rw.ui.*` rows above; representative: `packages/node_modules/@webex/react-component-activity-item/src/index.js`, `packages/node_modules/@webex/react-component-video/src/index.js` | +| `rw.state.modules` | state-management | public `@webex/redux-module-*` barrels | action/reducer/constant/helper/thunk exports are public semver surfaces; view selectors live in widget/container packages, not these barrels | individual exact paths in the `rw.state.*` rows above; representative: `packages/node_modules/@webex/redux-module-activity/src/index.js`, `packages/node_modules/@webex/redux-module-spaces/src/index.js` | +| `rw.state.sdk` | state-management | `@webex/react-redux-spark*` barrels | runtime and metrics exports are public; fixtures are test-oriented | `packages/node_modules/@webex/react-redux-spark/src/index.js`, `packages/node_modules/@webex/react-redux-spark-metrics/src/index.js`, `packages/node_modules/@webex/react-redux-spark-fixtures/src/index.js` | +| `rw.containers` | containers-hooks | seven public `@webex/react-container-*` entrypoints | each entrypoint is a public semver surface | individual exact paths in the `rw.container.*` rows above; representative: `packages/node_modules/@webex/react-container-activity-list/src/index.js`, `packages/node_modules/@webex/react-container-message-composer/src/index.js` | +| `rw.space.destinations` | space-messaging | `email`, `userId`, `spaceId`, `sip`, `pstn` where accepted | exact destination strings are stable | `packages/node_modules/@webex/widget-space/src/constants.js`, `packages/node_modules/@webex/widget-space/src/index.js` | +| `rw.space.events` | space-messaging | emitted Space/child events plus exported event constants | only events with an observed emitter are active host contracts | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | +| `rw.recents.options` | recents | public PropTypes/defaults and data-API options | accepted option names are compatibility surfaces; `muteNotifications` is accepted but unused | `packages/node_modules/@webex/widget-recents/src/container.js` | +| `rw.recents.events` | recents | current Recents host event set | exact emitted strings are stable | `packages/node_modules/@webex/widget-recents/src/events.js`, `packages/node_modules/@webex/widget-recents/src/container.js`, `packages/node_modules/@webex/widget-recents/src/enhancers/listeners.js` | +| `rw.meet.events` | meetings | legacy Meet call event constants | `calls:created/connected/disconnected` are emitted; membership constants have no emitter in current source | `packages/node_modules/@webex/widget-meet/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | + +### Browser and Data APIs + +| Contract ID | Owner | Surface | Purpose | Compatibility | Defined at | +|---|---|---|---|---|---| +| `rw.host.widget.select` | widget-runtime-auth | `window.webex.widget(element)` | select or create a mounted browser widget object | stable; `window.ciscospark` compatibility alias remains | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | +| `rw.host.widget.mount` | widget-runtime-auth | `widget.{name}Widget(options)` | render a registered widget into the selected element | widget names/options are public | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | +| `rw.host.widget.remove` | widget-runtime-auth | `widget.remove(callback?)` | unmount React and clear widget registry | always returns a Promise; no-callback form resolves to the unmount boolean; callback form invokes the callback with that boolean and leaves the Promise pending | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | +| `rw.host.data-api` | widget-runtime-auth | `[data-toggle^="webex-{name}"]` + `data-*` | auto-mount widgets at DOM ready | kebab-to-camel option mapping is public | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withDataAPI.js` | +| `rw.host.version` | build-release-tooling | `widgetFn.{name}.version` and widget object `.version` | expose bundle version to hosts | preserved with release metadata | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | + +### Events + +| Contract ID | Owner module | Event | Direction | Payload detail | Delivery guarantees | Compatibility | Defined at | +|---|---|---|---|---|---|---|---| +| `rw.event.messages-created` | Space, Recents | `messages:created` | publish | message/actor/room/person/file fields | synchronous callback + DOM/ampersand dispatch after internal handling | string stable; additive payload changes only | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.rooms-unread` | Space, Recents | `rooms:unread` | publish | room/space summary | emitted from realtime activity handling; not for current user's own message where implemented | string stable | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.rooms-read` | Space, Recents | `rooms:read` | publish | room/space summary | emitted after read-state transition | string stable | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.rooms-selected` | Recents | `rooms:selected` | publish | room summary; optional `action: call` | user-driven | string/action stable | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.calls-created` | Space, Recents | `calls:created` | publish | call plus available room ID | SDK/realtime driven | string stable | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | +| `rw.event.calls-connected` | Space | `calls:connected` | publish | call lifecycle data | SDK event driven | string stable | `packages/node_modules/@webex/widget-space/src/events.js` | +| `rw.event.calls-disconnected` | Space | `calls:disconnected` | publish | call lifecycle data | SDK event driven | string stable | `packages/node_modules/@webex/widget-space/src/events.js` | +| `rw.event.activity-changed` | Space | `activity:changed` | publish | selected activity | user/external-control driven | string stable | `packages/node_modules/@webex/widget-space/src/events.js` | +| `rw.event.memberships-created` | Recents | `memberships:created` | publish | membership/actor/room/person data | Mercury driven | string stable | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.memberships-deleted` | Recents | `memberships:deleted` | publish | membership/actor/room/person data | Mercury driven | string stable | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.add-clicked` | Recents | `add:clicked` | publish | empty object | user-driven; option-gated | string stable | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.profile-clicked` | Recents | `profile:clicked` | publish | current-user profile/avatar data | user-driven; option-gated | string stable | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rw.event.signout-clicked` | Recents | `user_signout:clicked` | publish | empty object | user-driven; option-gated | string stable | `packages/node_modules/@webex/widget-recents/src/events.js` | + +`packages/node_modules/@webex/widget-space/src/events.js` declares `calls:memberships:*` constants, and `packages/node_modules/@webex/widget-meet/src/events.js` declares unprefixed `memberships:*` constants. No publisher for those membership constants was found in current Space/Meet/Message source, so they are definition-only compatibility evidence and are excluded from the active event table. + +Legacy event guides also describe notification and mention events that are not present in the current Space/Recents event constant sets. They remain protected historical sources, not current contracts. + +### Commands & Flags + +| Contract ID | Owner | Command | Args / flags | Exit behavior | Compatibility | Defined at | +|---|---|---|---|---|---|---| +| `rw.cmd.install` | build-release-tooling | `npm install --legacy-peer-deps` | lockfile and legacy-peer resolution | nonzero on install error | CI dependency-install surface | `.circleci/config.yml` | +| `rw.cmd.build` | build-release-tooling | `npm run build {target}` | yargs target and command-specific args | nonzero on build error | root script surface | `package.json`, `scripts/build/index.js` | +| `rw.cmd.build-all` | build-release-tooling | `npm run build:all` | none | nonzero if a package build fails | root script surface | `package.json` | +| `rw.cmd.build-package` | build-release-tooling | `npm run build:package {name}` | package name | nonzero on unknown/build failure | root script surface | `package.json`, `scripts/build/commands/dist.js` | +| `rw.cmd.build-widgets` | build-release-tooling | `npm run build:widgets` | none | nonzero on widget build failure | root script surface | `scripts/build/commands/widgets.js` | +| `rw.cmd.build-journey` | build-release-tooling | `npm run build journey {path}` | target output path | nonzero on copy/build failure | browser-test artifact surface | `scripts/build/commands/journey.js` | +| `rw.cmd.serve` | build-release-tooling | `npm run serve {target}` | demo/package/samples target | nonzero on config/server failure | root script surface | `scripts/start/index.js` | +| `rw.cmd.start` | build-release-tooling | `npm start` | none | serves widget demo | root script surface | `package.json` | +| `rw.cmd.jest` | test-automation | `npm run jest [-- Jest args]` | Jest selectors/options | Jest exit code | root test surface | `package.json`, `jest.config.json` | +| `rw.cmd.static-analysis` | test-automation | `npm run static-analysis` | none | ESLint exit code | required gate | `package.json`, `.eslintrc.js` | +| `rw.cmd.journeys` | test-automation | `npm run test:automation[:smoke|:space|:recents]` | environment selects browser/Sauce | WebdriverIO exit code | environment-dependent | `package.json`, `wdio.conf.js` | +| `rw.cmd.tap` | test-automation | `npm run test:tap`, `npm run test:integration` | environment/target selection | WebdriverIO exit code | production/integration validation surface | `package.json`, `wdio.conf.js` | +| `rw.cmd.publish` | build-release-tooling | `npm run publish:components` | CI/registry environment | nonzero on build/publish failure | protected release operation | `package.json`, `scripts/utils/publish.js` | +| `rw.cmd.release` | build-release-tooling | `npm run release` | standard-version args | nonzero on version/changelog failure | Conventional Commits | `package.json` | + +## Requires — what this repo depends on + +| Dependency (service / package / datastore) | What is consumed | Schema / detail link | Availability assumption | Fallback on failure | Version floor | +|---|---|---|---|---|---| +| React/ReactDOM | component/rendering contract | `package.json` | host/build provides compatible runtime | none | `^16.8.4` | +| Webex SDK plugin family | auth, rooms, people, conversations, Mercury, meetings, teams, search, flags, metrics | `package.json` | network and valid credentials available for live behavior | loading/error UI; supplied SDK instance supported | `^2.60.4` family | +| Momentum UI / Webex Components | visual and calling component primitives | `package.json` | package resolution and compatible styles | none | pinned root versions | +| Browser DOM/media APIs | mount/events/audio/video/notifications/storage | module source | supported browser and permissions | error/disabled states; test fakes | browser matrix in `babel.config.js` | +| npm/AWS/CDN/CI/Sauce | artifact distribution and verification | `.circleci/config.yml`, `wdio.conf.js` | authenticated CI environment | local unit/static tests only; never claim remote validation | configured CI tool versions | + +## Compatibility & Deprecation Policy + +- **Breaking-change rule:** removing/renaming a package export, required prop, event string/payload guarantee, browser/data API, root command, or artifact path requires an approved spec delta, consumer migration note, and compatible major/versioned rollout. +- **Deprecation:** prefer additive optional props/exports; retain `@ciscospark/*` notices and browser alias while supported, and mark replacements before removal. + +## Detailed Interface Docs + +- Exact package declarations live in each `packages/node_modules/@webex/{package}/src/index.js|ts` and adjacent PropTypes/TypeScript files. +- Detailed behavior, state, flow, failure modes, and tests live in `ai-docs/modules/*-spec.md`. +- Current event construction lives in Space/Recents `src/events.js`; protected event guides are reconciled source material, not a substitute for current constants. + +## Maintenance + +- Add/change/remove a public surface only with synchronized updates to this catalog, the owning module spec, `.sdd/manifest.json`, and native entrypoint/type/event sources. +- Keep deprecated names in `GLOSSARY.md` and current runtime state in `SERVICE_STATE.md`. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/GETTING_STARTED.md b/ai-docs/GETTING_STARTED.md new file mode 100644 index 000000000..7dabf1fe6 --- /dev/null +++ b/ai-docs/GETTING_STARTED.md @@ -0,0 +1,86 @@ +<!-- ─────────────────────────────── + Template: Getting Started + Template-ID: getting-started + Generates: ai-docs/GETTING_STARTED.md + Description: Clone/build/run loop, toolchain, config/secrets, artifact registries, and multi-repo workspace layout. + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# Getting Started — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), route through [`SPEC_INDEX.md`](SPEC_INDEX.md), and use this page for the local build/test loop. + +## Prerequisites + +### Toolchain + +| Tool | Version | Where it's pinned | +|---|---|---| +| Node.js | 22 LTS (`lts/jod`) | `.nvmrc`; CI uses Node 22.22 | +| npm | 10 (CI) | `.circleci/config.yml` | + +### Access + +- Public npm registry read access for install. +- Webex integration credentials only for authenticated demos/journeys. +- Sauce credentials only for remote browser runs. +- npm publish token (`NPM_TOKEN`) only for release jobs. + +## Clone & Install + +```bash +git clone https://github.com/webex/react-widgets.git +cd react-widgets +nvm use +npm install --legacy-peer-deps +``` + +`npm install --legacy-peer-deps` matches `.circleci/config.yml`; `CONTRIBUTING.md` also documents root `npm install`. + +## Build / Run / Test + +| Role | Command | +|---|---| +| Install | `npm install --legacy-peer-deps` | +| Build (full packages) | `npm run build:all` | +| Package (one bundle) | `npm run build:package {package-name}` | +| Run (local) | `npm start` | +| Unit test | `npm run jest` | +| Integration / journey | `npm run test:automation:smoke` | +| Lint / format | `npm run static-analysis` | +| Combined static analysis + Jest | `npm test` | + +## First-Run Verification + +- `npm run static-analysis` must complete with no ESLint errors. +- `npm run jest` must pass the Jest suites rooted under `packages/node_modules/`. +- `npm start` should serve the widget demo at `http://localhost:8000`; authenticated widget behavior additionally requires valid Webex configuration. + +## Configuration & Secrets + +- Start from `.env.default` only as a key/reference list. Local `.env` may provide Webex client/token/destination values and Sauce or deployment credentials. +- Never commit populated `WEBEX_ACCESS_TOKEN`, `WEBEX_CLIENT_SECRET`, `SAUCE_ACCESS_KEY`, `NETLIFY_ACCESS_TOKEN`, npm tokens, AWS credentials, or private signing material. +- The Webex SDK/test helpers consume service URLs from environment configuration; use team-approved integration/test environments. + +### Artifact Registries + +| Registry | Host | Settings file | Auth env-var names (values NOT stored) | +|---|---|---|---| +| npm public | `registry.npmjs.org` | `.circleci/config.yml` (publish job writes `~/.npmrc`) | `NPM_TOKEN` | + +## Dev Environment + +- `npm start` runs `npm run serve demo widget-demo` through the Babel/yargs start tooling. +- `npm run serve:package {name}` uses `scripts/webpack/webpack.dev.babel.js` and webpack-dev-server. +- Journey runs can start a local static server through `wdio.conf.js`; `SAUCE=true` switches to Sauce Labs and Sauce Connect. + +## Where to Go Next + +- Agent rules: `../AGENTS.md`; system shape: `ARCHITECTURE.md`; module routing: `SPEC_INDEX.md`; test surface: `TEST_INDEX.md`. +- Build details: `modules/build-release-tooling-spec.md`; test setup: `modules/test-automation-spec.md`; conventions: `patterns/` and `rules/`. +- Committed SDD gate summary: [`SDD_BOOTSTRAP_EVIDENCE.md`](SDD_BOOTSTRAP_EVIDENCE.md). + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-09-03`. diff --git a/ai-docs/GLOSSARY.md b/ai-docs/GLOSSARY.md new file mode 100644 index 000000000..1448351ad --- /dev/null +++ b/ai-docs/GLOSSARY.md @@ -0,0 +1,68 @@ +<!-- ─────────────────────────────── + Template: Glossary + Template-ID: glossary + Generates: ai-docs/GLOSSARY.md + Description: Ubiquitous language — domain term → definition → authoritative code location. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Glossary — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md) and [`SPEC_INDEX.md`](SPEC_INDEX.md). Use these code-grounded terms rather than inventing synonyms. + +## Domain Terms + +| Term | Definition | Authoritative location | Notes / synonyms to avoid | +|---|---|---|---| +| Widget | A self-contained React capability composed with Webex runtime/state integrations and optionally registered for browser/data APIs. | `packages/node_modules/@webex/webex-widget-base/src/index.js` | Not every `react-component-*` package is a widget. | +| Space | The client representation of a Webex room/conversation used by Space and Recents behavior. | `packages/node_modules/@webex/redux-module-spaces/src/reducer.js` | Current public events still use the compatibility resource name `rooms`. | +| Activity | A normalized conversation item such as a message, share, or system activity. | `packages/node_modules/@webex/redux-module-activities/src/reducer.js` | Do not use “message” for every activity type. | +| Destination | A typed widget target identified by email, user ID, space ID, SIP address, or PSTN value where supported. | `packages/node_modules/@webex/widget-space/src/constants.js` | Preserve exact destination strings: `email`, `userId`, `spaceId`, `sip`, `pstn`. | +| Browser global API | `window.webex.widget(element)` and registered `{name}Widget` functions used to mount/remove widgets and receive events. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | `window.ciscospark` is a compatibility alias, not the preferred name. | +| Data API | Auto-mount behavior driven by a `data-toggle="webex-{name}"` element and other `data-*` options. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withDataAPI.js` | Do not confuse with Webex REST APIs. | +| Mercury | The Webex SDK realtime channel used for conversation/activity events. | `packages/node_modules/@webex/redux-module-mercury/src/actions.js` | Not a queue owned by this repository. | +| SDK instance | A supplied or constructed authenticated Webex JavaScript SDK object used by thunks and adapters. | `packages/node_modules/@webex/react-redux-spark/src/sdk.js` | Older code often names the variable `sparkInstance`; it still represents Webex SDK state. | +| Basic mode | Recents loading through Webex REST rather than encrypted conversation flows. | `packages/node_modules/@webex/widget-recents/src/enhancers/setup.js` | The source docs explicitly warn that this removes end-to-end encryption. | +| Journey test | A WebdriverIO browser integration scenario covering mounted widgets against local/TAP/integration environments. | `test/journeys/`, `wdio.conf.js` | Not a Jest unit test. | +| SRI manifest | Versioned CDN file catalog containing integrity hashes and signatures for distributable files. | `scripts/utils/sri.js` | Not `.sdd/manifest.json`. | + +## Abbreviations & Acronyms + +| Abbreviation | Expansion | Meaning in this repo | +|---|---|---| +| SDK | Software Development Kit | Webex JavaScript SDK and calling component adapters consumed by widgets. | +| SRI | Subresource Integrity | Hash/signature metadata generated for CDN widget artifacts. | +| HOC | Higher-Order Component | A React wrapper that injects state, SDK behavior, scrolling, or host integration. | +| TAP | Test Against Production | Journey suites that use deployed CDN artifacts rather than local bundles. | +| PSTN | Public Switched Telephone Network | A supported calling destination type in Space/Meet/Meetings/number-pad flows. | +| SIP | Session Initiation Protocol | A string destination type passed to SDK calling/meeting behavior; this repo does not implement SIP framing. | + +## Context-Specific Meanings + +| Term | Context / module | Meaning here | +|---|---|---| +| `store` | Redux packages | The in-memory Redux state tree. | +| `store` | browser runtime | `window.webex.widgetStore`, a registry of mounted widget objects. | +| `event` | widget contract | A host callback/DOM/ampersand notification such as `messages:created`. | +| `event` | SDK/realtime flow | A Webex SDK/Mercury notification consumed internally and sometimes translated to a host event. | +| `meeting` / `call` | Meetings packages | SDK meeting object plus Redux IDs/media readiness. | +| `meeting` / `call` | legacy media packages | Legacy media/call records and events used by Space/Recents/Meet. | + +## Deprecated / Renamed Terms + +| Old term | Current term | Why renamed | Still appears in | +|---|---|---|---| +| Cisco Spark | Webex | Product/package namespace migration. | Protected `@ciscospark/*` compatibility READMEs and legacy identifiers. | +| `@ciscospark/*` | `@webex/*` | npm package namespace migration. | `packages/node_modules/@ciscospark/*/README.md`, Jest compatibility mappings. | +| `sparkInstance` / `sparkState` | Webex SDK instance/state | Historical source naming retained for compatibility. | legacy widget, Redux, and container code. | +| rooms | spaces | Current product terminology; event contract strings remain `rooms:*`. | event constants and payload fields. | + +## Maintenance + +- Add a term in the same change that introduces a public entity, event, state, destination, or build artifact. +- Cross-reference public surfaces in `CONTRACTS.md` and client state ownership in `ARCHITECTURE.md`. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/REVIEW_CHECKLIST.md b/ai-docs/REVIEW_CHECKLIST.md new file mode 100644 index 000000000..368b00bc1 --- /dev/null +++ b/ai-docs/REVIEW_CHECKLIST.md @@ -0,0 +1,57 @@ +<!-- ─────────────────────────────── + Template: Review-Check Catalog + Template-ID: review-checklist + Generates: ai-docs/REVIEW_CHECKLIST.md + Description: The review checks — 6 core + 4 coverage-conditional + 3 cross-cutting — selected by manifest coverage state. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Review-Check Catalog — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), route through [`SPEC_INDEX.md`](SPEC_INDEX.md), and apply this catalog before merge. + +Each finding records severity, check id, file path, impact, and 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 | Behavior/public-surface changes update SDD docs and every requirement states WHAT and WHY. | Blocking | +| C2 | Contract correctness | Package exports, props, widget/data/browser APIs, events, commands, and dependencies are complete and compatible. | Blocking | +| C3 | Code-vs-spec match | Signatures, event strings, state/flow, and architecture claims match source and tests. | Blocking | +| C4 | Test adequacy | Positive and negative unit coverage exists; relevant journey/accessibility coverage is run or its gap recorded. | Important | +| C5 | Error handling + input validation | Host/SDK inputs are validated and promise/event/UI failure paths are not swallowed. | Important | +| C6 | Security baseline | No secrets; credential, rendered-content, file/media, encrypted-mode, and release controls remain intact. | Blocking | + +## Coverage-conditional checks + +| # | Check | When it applies | What it verifies | Severity | +|---|---|---|---|---| +| K1 | Regression guard | Any Partial/Untracked module or MODIFIED/REMOVED guarantee | A characterization/targeted baseline protects unchanged behavior with positive and negative cases. | Blocking | +| K2 | Grounding | Partial/Untracked module | Claims cite real source/tests and uncovered surfaces remain explicit. | Important | +| K3 | Drift threshold | Any tracked module | Drift remains within the manifest state threshold. | Important | +| K4 | Coverage-state accuracy | Coverage state changes | Score, gaps, drift, promotion history, and waiver rules support the state. | Medium | + +## Cross-cutting checks + +| # | Check | What it verifies | Severity | +|---|---|---|---| +| X1 | Independent review | Validator runtime differs from generator runtime as `.sdd/manifest.json` requires. | Blocking | +| X2 | Observability | SDK/Redux/metrics/CI signals cover the change without sensitive logging. | Medium | +| X3 | Rollout safety | Package/CDN compatibility, SRI, default props/flags, and consumer transition/rollback are safe. | Important | + +## How the set is selected + +1. Run all six core checks. +2. Add coverage checks for every touched module; all bootstrap modules are currently Partial. +3. Add cross-cutting checks for public contracts, auth/security, async/realtime behavior, build/release, or higher-autonomy work. + +## Output + +- Produce a draft compliance matrix, severity-sorted findings, and Pass / Pass-with-warnings / Blocked verdict. +- Keep local validation drafts ignored and uncommitted; a human decides whether anything is posted externally. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/RULES.md b/ai-docs/RULES.md new file mode 100644 index 000000000..7341b06e7 --- /dev/null +++ b/ai-docs/RULES.md @@ -0,0 +1,111 @@ +<!-- ─────────────────────────────── + Template: RULES + Template-ID: rules + Generates: ai-docs/RULES.md + Description: Enforceable do/don't beyond AGENTS — coverage, autonomy, naming, logging, errors, testing, security, drift, secrets. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Rules — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), route through [`SPEC_INDEX.md`](SPEC_INDEX.md), and use detailed rules under `rules/` only when relevant. + +## Coverage Map (which docs/specs to trust) + +| Module | Manifest coverage state | What it means here | +|---|---|---| +| Space and messaging | Partial | High first-pass coverage; independent spec-validator Pass at `548a0312`; code cross-checking and the history promotion gate still apply. | +| Recents | Partial | High first-pass coverage; verify event names/payloads against current constants and listeners. | +| Meetings | Partial | High first-pass coverage; verify SDK lifecycle behavior and known unimplemented lifecycle branches in code. | +| Calling widgets | Partial | High first-pass coverage; verify typed adapter/component contracts and sparse tests. | +| Shared UI components | Partial | Package entrypoints are indexed; cross-check component props and adjacent tests. | +| Redux and state management | Partial | Actions/reducers are indexed; cross-check each thunk and state record before behavior changes. | +| Containers and HOCs | Partial | Entrypoints and flows are documented; cross-check wrapped-component contracts. | +| Widget runtime/auth/demos | Partial | Host/auth contracts are detailed; resolve the default-helper discrepancy before relying on that shortcut. | +| Build and release tooling | Partial | Commands and artifact flow are documented; validate CI/deployment changes against current scripts. | +| Test automation | Partial | Suites and intent are reconciled; verify environment-dependent journeys in their target environment. | + +## Autonomy & Ask-First + +- **May proceed:** docs-only corrections, tests for already documented behavior, and internal refactors that preserve exported contracts after an approved plan. +- **Ask first / plan + confirm:** package exports, component props, events, data/browser globals, SDK auth, state shape, destination behavior, build/publish/deploy flow, or test-policy changes. +- **Never without explicit human approval:** push, publish, deploy, delete/overwrite protected docs, rotate credentials, or intentionally break a consumer contract. + +## Naming + +- Published packages use the `@webex/` namespace and kebab-case package directories; legacy `@ciscospark/` names are compatibility references only. +- React component exports use PascalCase; HOCs/hooks use `withX`, `injectX`, or `useX`; Redux action constants are upper snake case with a module prefix in their string values. +- Public widget event strings use `{resource}:{event}` lowercase names such as `messages:created`; never rename a string while only changing its constant identifier. + +## Logging + +- Use the supplied SDK logger for product diagnostics and existing Redux logging only in non-production stores. +- Never log credentials, private message/file content, or raw call/media objects. Preserve the Recents call-object omission when enriching event logs. +- Logging rules are review-enforced; ESLint prevents stray patterns only where configured. + +## Error Handling + +- Async Redux/SDK operations must reject or dispatch explicit error/status actions; UI boundaries render `ErrorDisplay` or typed error states. +- Do not swallow promise failures. Where legacy code currently logs-and-continues (for example optional flags), preserve that behavior unless a spec change approves stricter propagation. +- Calling adapters/hooks must handle missing adapter functions and promise failures without leaving loading/playing/form state stuck. + +## Imports / Dependencies + +- Follow ESLint `import/order`: builtin, external, internal, parent, sibling, index with blank lines between groups. +- Widgets may depend on base/runtime, containers, Redux modules, and components; reusable components must not acquire widget-specific state ownership without an approved boundary change. +- Add or upgrade dependencies only at root `package.json`; verify package output externalization and Webex SDK version-family compatibility. + +## Testing + +- Add positive and negative unit coverage near changed package source; Jest discovers `*.test.js` under `packages/node_modules/`. +- Changes to Space/Recents host behavior, auth, calls, files, roster, accessibility, or startup options identify and run the appropriate journey suite. +- Required local gates are `npm run static-analysis` and `npm run jest`; environment-dependent WebdriverIO gaps must be reported, not silently treated as passing. + +## Security + +- Treat props, data attributes, SDK/realtime payloads, file/card content, and environment variables as boundary inputs. +- Never hardcode or log secrets. Preserve encrypted default flows and SRI generation; see `SECURITY.md`. + +## Spec-Currency & Drift Thresholds + +- Update the owning module spec, `CONTRACTS.md`/`SERVICE_STATE.md` when applicable, `SPEC_INDEX.md`, and `.sdd/manifest.json` in the same behavior-changing change. +- Drift thresholds: Specced ≤5%, Partial ≤15%, Untracked ≤25%. Promotion additionally follows `.sdd/coverage-policy.defaults.yaml`. + +## Secrets Policy + +- Secrets are injected by the host, `.env`, or CI secret stores. `.env` is ignored; `.env.default` contains names/default service URLs only. +- Never commit tokens, keys, client secrets, deployment credentials, private signing keys, or populated test-user data. + +## Concurrency & Async + +- Promise thunks and SDK event listeners must keep loading/connecting flags consistent on success and failure. +- Register realtime/media/browser listeners once and remove them on teardown; do not duplicate host events during React lifecycle updates. +- Preserve event ordering assumptions: store normalized state before consumers render or host callbacks observe the corresponding transition. + +## Strict-Compliance Mode + +- CI, release, SDD validation, and automated changes stop on the first Blocking contract, security, source-fidelity, conformance, or code/spec finding. +- Independent validator findings remain local drafts until a human approves publication. + +## Enforcement Metadata + +| Rule area | Source evidence | Severity | Owner | Verification | +|---|---|---|---|---| +| Public naming and entrypoints | `packages/node_modules/@webex/widget-space/src/index.js`, `packages/node_modules/@webex/widget-speed-dial/src/index.ts`, `packages/node_modules/@webex/widget-space/src/events.js`, `package.json` | Blocking for incompatible public changes | owning package maintainers | contract review, static analysis, relevant package tests | +| Logging and credential handling | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js`, `packages/node_modules/@webex/widget-recents/src/events.js`, `.eslintrc.js` | Blocking for secret exposure; Important otherwise | runtime and capability maintainers | security review and static analysis | +| Async error handling | `packages/node_modules/@webex/redux-module-spaces/src/actions.js`, `packages/node_modules/@webex/widget-meetings/src/handlers/index.js` | Important; Blocking when a public operation falsely reports success | owning state/widget maintainers | positive/negative Jest coverage and relevant journeys | +| Dependency boundaries | `.eslintrc.js`, `package.json`, `scripts/utils/package.js` | Important | repository/tooling maintainers | ESLint, build-target verification, compatibility review | +| Testing and accessibility | `jest.config.json`, `wdio.conf.js`, `test/journeys/testplan.md` | Blocking for required gate failure | repository and owning module maintainers | `npm run static-analysis`, `npm run jest`, focused journey/axe suite | +| Secrets and release operations | `.gitignore`, `.circleci/config.yml`, `scripts/build/commands/sri.js` | Blocking | security and release maintainers | secret scan/review, CI release gates, no local publish/deploy verification | +| Concurrency and cleanup | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`, `packages/node_modules/@webex/widget-voice-mail/src/hooks/useAudio.ts`, `packages/node_modules/@webex/widget-space/src/container.js` | Important; Blocking for credential/resource leakage | owning runtime/module maintainers | lifecycle tests, listener audit, repeated mount/unmount characterization | +| Spec currency and strict compliance | `.sdd/manifest.json`, `ai-docs/REVIEW_CHECKLIST.md` | Blocking when configured thresholds or validation gates fail | repository maintainers | drift check, conformance, coverage review, independent validator | + +## Maintenance + +- Add a rule only when code/history shows a recurring repository constraint; defer formatting/import mechanics to ESLint. +- Cross-reference examples in `patterns/` and fuller rules in `rules/`. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/SDD_BOOTSTRAP_EVIDENCE.md b/ai-docs/SDD_BOOTSTRAP_EVIDENCE.md new file mode 100644 index 000000000..e9960cab8 --- /dev/null +++ b/ai-docs/SDD_BOOTSTRAP_EVIDENCE.md @@ -0,0 +1,72 @@ +# SDD Bootstrap Evidence — react-widgets + +> Committed summary for reviewers. Full gate reports are generated locally under `.generated/sdd/` (gitignored). + +## Bootstrap run + +| Field | Value | +|---|---| +| Run date | 2026-07-22 (0.2.2 upgrade + akulakum review closure: 2026-09-03) | +| Mode | SDD Stage 0 rigorous, **reconcile** policy | +| Template library | **0.2.2** (`0aa65d9`) | +| Generator runtime | cursor-agent (0.2.2 follow-up / Session B preflight) | +| Validator runtime | codex-desktop | +| Validated source commit | `548a0312718835b08faf225187951a05eabc42ba` | +| Branch | `react-widgets-sdd-022` (tracks `vivekv1504/react-widgets-migrate-into-sdd-bootstrap`) | +| PR | https://github.com/webex/react-widgets/pull/1468 | + +## Module map + +Ten capability-level modules with canonical specs under [`ai-docs/modules/`](modules/). Standing docs under `ai-docs/`. Repo-wide test router: [`TEST_INDEX.md`](TEST_INDEX.md). All modules are top-level (`Parent spec` = `—`, `has_submodules: false`). + +## Reconcile source fidelity + +Protected README, event-guide, namespace-migration, and journey-testplan source files remain unchanged. Unit disposition: + +| Result | Count | +|---|---:| +| Inventories passed | 10/10 | +| Total source units | 709 | +| Placed in canonical sections | 148 | +| Native references retained | 412 | +| Stale with code evidence | 19 | +| Not applicable with rationale | 130 | +| Conflicts | 0 | +| Unresolved | 0 | + +## Gate outcomes + +| Gate | Verdict | Notes | +|---|---|---| +| Brownfield questionnaire | Pass | CRITICAL repo/module fields answered with code/source evidence on 2026-07-22 | +| Source-fidelity review | Pass | 10/10 inventories; 709 units; 0 unresolved / 0 conflicts | +| Generated-doc-conformance | **Pass, 0 Blocking** | 28/28 template-backed files; 0.2.2 headers, Parent spec, TEST_INDEX, standing-doc shape. Local report: `.generated/sdd/conformance/bootstrap-022-2026-09-03-cursor.md` (gitignored) | +| Manifest schema | Pass | Validates against repo-pinned [`.sdd/config/sdd-manifest.schema.json`](../.sdd/config/sdd-manifest.schema.json) (0.2.2 extensions: `test_index_path`, `template_library_*`, `has_submodules`, validation status fields) | +| Coverage review | Partial retained | 91–96% documentation scores; last-five-PR promotion-history gate and characterization gaps remain | +| Spec-validator (Axis A + B) | **Pass** | Codex Session B at `548a0312`; 0 Blocking, 0 warnings; 10 modules remain Partial | + +## akulakum PR #1468 review closure (2026-09-03) + +| Thread | Fix | +|---|---| +| Validation provenance / SHA drift | Pinned after Session B: `source_commit` `548a0312`, status `pass`, 10 module Validation rows aligned | +| Template metadata blocks | Restored/upgraded hidden blocks to template-library **0.2.2** on generated Markdown | +| Reconcile evidence | This file records the 709-unit summary so A14 is reviewable without `.generated/` paths | +| CONTRACTS Requires columns | Kept template headers; library version bumped to 0.2.2 | +| Partial despite 91–96% | Unchanged: weak/characterization evidence and five-PR promotion gate | +| K1 REVIEW_CHECKLIST | No change (positive) | + +## Reproducing locally + +See [GETTING_STARTED.md](GETTING_STARTED.md). Requires SDD skills installed locally (`.cursor/`, `.agents/`, or `.claude/` — not committed). + +After rerun, inspect: + +- `.generated/sdd/bootstrap-questionnaire.md` +- `.generated/sdd/source-fidelity/` +- `.generated/sdd/conformance/<run-id>.md` +- `.generated/sdd/validation/<date>.md` + +## Protected sources (unchanged) + +`README.md`, package READMEs, Space/Recents `events.md`, `@ciscospark/*` rename notices, and `test/journeys/testplan.md` were not rewritten. diff --git a/ai-docs/SECURITY.md b/ai-docs/SECURITY.md new file mode 100644 index 000000000..24dd55ed0 --- /dev/null +++ b/ai-docs/SECURITY.md @@ -0,0 +1,82 @@ +<!-- ─────────────────────────────── + Template: Security Baseline + Template-ID: security + Generates: ai-docs/SECURITY.md + Description: Standing security posture — trust boundaries, authn/authz, secret handling, data classification. + Library ver: 0.2.2 + Last updated: 2026-07-11 +─────────────────────────────── --> + +# Security Baseline — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), [`SPEC_INDEX.md`](SPEC_INDEX.md), and [`ARCHITECTURE.md`](ARCHITECTURE.md). Read this before changing authentication, host input, SDK calls, rendered content, or deployment credentials. + +## Trust Boundaries + +| Boundary | Untrusted side | Trusted side | Enforced at the crossing | +|---|---|---|---| +| React widget props | embedding application | widget/runtime | PropTypes or TypeScript props; destination/setup validation; error UI for invalid state. | +| Browser/data API | DOM attributes, host callbacks, selected element | `webex-widget-base` | Element check, known widget registration, data attribute normalization, component contracts. | +| Credentials/SDK | host-supplied token, guest token, or SDK instance | SDK-backed widget state | Webex SDK auth/device setup; never treat presence of a string as authorization. | +| Realtime/network data | Webex services and Mercury events | Redux/modules/components | SDK processing, resource normalization, action/reducer boundaries, render encoding. | +| Adaptive Card/markdown/file content | message/file payloads | rendered browser UI/download | parsing helpers, supported-action checks, React rendering, file retrieval helpers. | +| CI/release environment | secret variables and external registries/cloud | build/publish/deploy scripts | CI secret injection; no credentials in tracked files or logs. | + +## Authentication & Authorization Model + +- **Authentication:** legacy widgets accept `accessToken`, `guestToken`, or an already authenticated SDK instance; SDK setup lives under `packages/node_modules/@webex/react-redux-spark/`. +- **Authorization:** Webex services/plugins enforce scopes and resource access. The widget repository selects SDK operations but does not implement a separate RBAC/ABAC layer. +- **Default posture:** without authenticated/registered SDK state, setup enhancers do not start resource loading or Mercury flows. Evidence: `packages/node_modules/@webex/widget-space/src/enhancers/setup.js`. + +## Secret & Credential Handling + +- Secrets come from the embedding host or CI/local environment, never source code. +- Runtime props inject access/guest tokens or SDK objects; `.env` supports local/test/build tooling and is ignored. +- Rotation policy is owned by the credential issuer/CI platform; this repository contains no rotation implementation. +- **Hard rule:** never commit or log tokens, client secrets, Sauce keys, npm tokens, Netlify tokens, AWS credentials, or private signing keys. + +## Data Classification & Handling + +| Data class | Examples | Storage rule | Logging rule | In transit | +|---|---|---|---|---| +| Credentials | access/guest tokens, client secret, service keys | host/CI memory only; no tracked files | never log | Webex/CI HTTPS mechanisms | +| User/conversation data | names, email addresses, IDs, messages, files, memberships | transient Redux/React/SDK state; remote service remains system of record | avoid payload logs; redact credentials and sensitive content | SDK-managed HTTPS/Mercury | +| Call/media data | call objects, media streams, voicemail audio | browser memory/media elements | do not serialize raw call/media objects | SDK/browser media transport | +| Build/release metadata | versions, hashes, signatures, artifact URLs | generated build/CDN manifests | safe unless paired with credentials | registry/CDN HTTPS | + +## Input Validation & Output Encoding Posture + +- Validate destination types/IDs, props, event payload assumptions, and adapter availability before SDK operations. +- Keep React rendering/escaping intact. Do not insert untrusted HTML except through the existing reviewed markdown/adaptive-card paths. +- Restrict file handling to SDK/file helper outputs; preserve filename/download and supported-card-action validation. + +## Transport & Headers + +- The library consumes Webex and deployment services over their SDK/HTTPS clients; it does not own an HTTP server or CORS policy. +- CDN hosts must preserve SRI usage and HTTPS URLs. Local demo/browser test servers are development-only and do not define production security headers. + +## Session & Cookie Posture + +- Production widget packages do not establish a server session or own authentication cookies. +- The private demo stores configuration, including an access token, through `react-cookie` in `packages/node_modules/@webex/widget-demo/src/components/demo-widget/index.js`; this is development-only behavior, not a secure production token-storage contract. +- Hosts must not copy demo cookie persistence into production integrations. Production credentials stay in the embedding application's approved credential lifecycle and should be supplied through an authenticated SDK instance when possible. + +## Known Sensitive Areas & Accepted Risks + +| Area | Risk | Mitigation / current posture | Owner | +|---|---|---|---| +| Data-attribute token configuration | Credentials can be visible in DOM/source | Prefer imported React/SDK-instance integration for sensitive hosts; never place real tokens in committed examples. | embedding application | +| Demo cookie persistence | The private demo persists its access token in a browser cookie without defining production cookie flags/rotation | Keep the demo private and development-only; clear cookies in automated journeys; never use this storage design in a production host. | widget runtime maintainers | +| Legacy browser global alias | Global mutable namespace can be modified by other scripts | Validate target element, register once with warning, and remove store entries on teardown. | widget runtime maintainers | +| Recents `basicMode` | REST loading removes end-to-end encryption behavior | Default is `false`; document and intentionally opt in. | Recents consumers | +| Legacy/default base helper discrepancy | README quick start and current implementation disagree | Treat current code as source of truth; prefer object-shaped `constructWebexEnhancer` until separately fixed and validated. | widget runtime maintainers | +| Client logging | SDK/event payloads may contain user data | Use SDK levels, omit raw call object from Recents event logs, and never log tokens. | module maintainers | + +## Reporting & Review + +- Authentication, rendered untrusted content, browser globals, file/media handling, dependencies, and release-secret changes require security-aware review plus static analysis and relevant tests. +- Report suspected vulnerabilities through the Webex developer support/security process; do not disclose credentials or exploit details in public issues. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/SERVICE_STATE.md b/ai-docs/SERVICE_STATE.md new file mode 100644 index 000000000..846228413 --- /dev/null +++ b/ai-docs/SERVICE_STATE.md @@ -0,0 +1,68 @@ +<!-- ─────────────────────────────── + Template: Service State (living) + Template-ID: service-state + Generates: ai-docs/SERVICE_STATE.md + Description: Living as-built registry — current endpoints/events/stores/deps/limits/metrics/flags; read first to avoid duplicates. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Service State (living) — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md), [`SPEC_INDEX.md`](SPEC_INDEX.md), and [`ARCHITECTURE.md`](ARCHITECTURE.md). Despite the template name, this is the library's current as-built surface registry. + +## Current Events + +| Event / topic | Direction | Producer/consumer | Payload reference | +|---|---|---|---| +| `messages:created` | publish to host | Space and Recents | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rooms:unread`, `rooms:read` | publish to host | Space and Recents | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | +| `rooms:selected` | publish to host | Recents | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `calls:created` | publish to host | Space/Recents legacy calling flow | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-recents/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | +| `calls:connected`, `calls:disconnected` | publish to host | Space | `packages/node_modules/@webex/widget-space/src/events.js` | +| `memberships:created`, `memberships:deleted` | publish to host | Recents | `packages/node_modules/@webex/widget-recents/src/events.js` | +| `activity:changed` | publish to host | Space | `packages/node_modules/@webex/widget-space/src/events.js` | +| `add:clicked`, `profile:clicked`, `user_signout:clicked` | publish to host | Recents | `packages/node_modules/@webex/widget-recents/src/events.js` | +| Webex Mercury activities | consume | Redux Mercury/Space/Recents packages | `packages/node_modules/@webex/redux-module-mercury/src/actions.js` | +| meeting `media:ready` | consume | meetings Redux package | `packages/node_modules/@webex/redux-module-meetings/src/actions.js` | + +Space declares `calls:memberships:*` constants and legacy Meet declares `memberships:*` constants, but no publisher for either membership set was found in current source. They are intentionally excluded from this current-events registry. + +## Data Stores + +| Store | Purpose | Owned by this library? | +|---|---|---| +| per-widget Redux store | immutable client resource/status state on the default Provider path; `withInitialState` skips Provider when `props.spark` is supplied | yes, in-memory only | +| `window.webex.widgetStore` | mounted browser-widget registry | yes, in-memory only | +| browser `localStorage` focus flags | number-pad focus handoff | yes, temporary keys only | +| Webex service data | conversations, spaces, users, teams, calls, meetings | no; consumed through SDK/services | + +## External Dependencies + +| Dependency | Used for | Timeout / retry | Circuit breaker / fallback | +|---|---|---|---| +| Webex SDK/services | auth, registration, rooms/conversations, people, teams, meetings, realtime, search, flags, metrics | SDK/plugin-owned; widget thunks expose promise state | loading/error UI; optional features may log and continue | +| Browser media/notification APIs | calls, audio/video, notifications | browser/SDK-owned | permission or capability failures surface through UI/status | +| npm registry | published package delivery | npm tooling | build/publish fails; no silent fallback | +| AWS S3/CloudFront | CDN widget artifacts | CircleCI/orb tooling | deployment job fails; archives remain versioned | +| Sauce Labs/Selenium | browser journeys | WebdriverIO retries connections up to three times | local Selenium when Sauce is disabled | + +## Feature Flags (current) + +| Flag / option | Gates | Current default | Owner | Safe to remove when | +|---|---|---|---|---| +| Recents `basicMode` | REST vs encrypted initial-space loading | `false` | Recents | only after a contract change removes the alternate mode | +| `enableAddButton` | Recents add-space control/event | `false` | Recents | after consumer migration and major-version removal | +| `enableSpaceListFilter` | Recents filter UI | `true` | Recents | after consumer migration and major-version removal | +| `enableUserProfile` / `enableUserProfileMenu` | Recents profile controls/events | `true` / `false` | Recents | after consumer migration and major-version removal | +| Space `spaceActivities` / `composerActions` | activity and composer controls | enabled | Space | after consumer migration and major-version removal | +| SDK feature/flag values | Webex-service-driven behavior | service-defined | Webex services | only with upstream contract approval | + +## Maintenance + +- Update the relevant row in the same change that changes an export, event, host option, state store, dependency, or feature flag. +- Stable catalog: `CONTRACTS.md`; security posture: `SECURITY.md`; module details: `modules/`. + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-08-07`. diff --git a/ai-docs/SPEC_INDEX.md b/ai-docs/SPEC_INDEX.md new file mode 100644 index 000000000..56782fd52 --- /dev/null +++ b/ai-docs/SPEC_INDEX.md @@ -0,0 +1,75 @@ +<!-- ─────────────────────────────── + Template: Spec Index + Template-ID: spec-index + Generates: ai-docs/SPEC_INDEX.md + Description: Router — which docs to load for which task and the canonical module registry. + Library ver: 0.2.2 + Last updated: 2026-07-31 +─────────────────────────────── --> + +# Spec Index — react-widgets + +> Start with root [`AGENTS.md`](../AGENTS.md). This is the human router; `.sdd/manifest.json` is the machine source of truth. + +## Module Registry + +| Module | Responsibility | Manifest coverage state | Start here | +|---|---|---|---| +| Space and messaging | Space, message, files, roster, activity composition, and host events. | Partial (94%) | `modules/space-messaging-spec.md` | +| Recents | Space-list loading/filtering, realtime updates, selection/call/profile host events. | Partial (95%) | `modules/recents-spec.md` | +| Meetings | Destination lookup, create/join/media/leave lifecycle, and meeting UI. | Partial (92%) | `modules/meetings-spec.md` | +| Calling widgets | Call history, number pad, speed dial, voicemail, typed adapters/hooks. | Partial (91%) | `modules/calling-spec.md` | +| Shared UI components | Public/private presentational components and UI utilities. | Partial (93%) | `modules/shared-ui-components-spec.md` | +| Redux and state management | Immutable state, SDK thunks, reducers, selectors, metrics, fixtures. | Partial (94%) | `modules/state-management-spec.md` | +| Containers and HOCs | State/SDK-connected UI, downloads, notifications, presence, scrolling, Mercury. | Partial (92%) | `modules/containers-hooks-spec.md` | +| Widget runtime/auth/demos | Auth/SDK setup, browser/data APIs, teardown, sign-in, demos, samples. | Partial (93%) | `modules/widget-runtime-auth-spec.md` | +| Build and release tooling | Build/transpile/bundle/SRI/serve/publish/deploy/release workflows. | Partial (95%) | `modules/build-release-tooling-spec.md` | +| Test automation | Jest, journeys, smoke/TAP/integration/accessibility, CI test reporting. | Partial (96%) | `modules/test-automation-spec.md` | + +All scores were assessed 2026-07-22 from package entrypoints, events/commands, implementation flows, tests, and reconciled sources. Independent spec-validator **Pass** at `548a0312` (validator `codex-desktop`); committed reconcile/conformance summary is in [`SDD_BOOTSTRAP_EVIDENCE.md`](SDD_BOOTSTRAP_EVIDENCE.md). Status remains Partial because documented weak-evidence gaps and the five-PR promotion-history gate remain. + +## Task Routing + +| If the task is… | Load | +|---|---| +| Understanding the system | `ARCHITECTURE.md` | +| Changing a package/widget behavior | the owning `modules/*-spec.md` plus `CONTRACTS.md` | +| Changing host/browser/data API or auth | `modules/widget-runtime-auth-spec.md` + `SECURITY.md` | +| Changing exported events | owning widget spec + `CONTRACTS.md` + `SERVICE_STATE.md` | +| Changing build/release/CDN behavior | `modules/build-release-tooling-spec.md` + `SECURITY.md` | +| Changing tests or CI verification | `TEST_INDEX.md` + `modules/test-automation-spec.md` + `REVIEW_CHECKLIST.md` | +| Running or changing tests | `TEST_INDEX.md` + the affected module spec | +| Updating docs after code | affected module specs, standing indexes, manifest, and source-fidelity policy if protected docs are involved | + +## Incident History + +No tracked incident/RCA index exists in this repository. Do not infer incidents from generic warnings; add reference rows only when an authoritative ticket/RCA is supplied. + +## Phase-Based Loading Protocol + +| Phase | Load | +|---|---| +| Orient | `AGENTS.md` + this file | +| Specify | affected module spec, `CONTRACTS.md`, and focused standing docs | +| Build | affected spec + `patterns/`/`rules/` + real source/tests | +| Verify | `REVIEW_CHECKLIST.md`, affected specs, manifest, and independent validation report | + +## Spec Registry + +| Doc | Location | Purpose | +|---|---|---| +| Architecture | `ARCHITECTURE.md` | system/package shape, state, interactions, host and release boundaries | +| Patterns | `patterns/` | code-grounded correct/incorrect conventions | +| Rules | `RULES.md` + `rules/` | enforceable repository constraints | +| Glossary | `GLOSSARY.md` | domain language and compatibility names | +| Security | `SECURITY.md` | trust boundaries, credentials, rendered data, release secrets | +| Contracts | `CONTRACTS.md` | package, event, browser/data API, and command catalog | +| Service state | `SERVICE_STATE.md` | living events/stores/dependencies/flags registry | +| Test index | `TEST_INDEX.md` | test tiers, canonical commands, locations, frameworks, dependencies, and quality gates | +| Getting started | `GETTING_STARTED.md` | install/build/run/test loop | +| Decisions | `adr/` | durable architecture decisions | +| Review catalog | `REVIEW_CHECKLIST.md` | merge gate selection and finding format | + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-09-03`. diff --git a/ai-docs/TEST_INDEX.md b/ai-docs/TEST_INDEX.md new file mode 100644 index 000000000..df4f3ff06 --- /dev/null +++ b/ai-docs/TEST_INDEX.md @@ -0,0 +1,52 @@ +<!-- ─────────────────────────────── + Template: Test Index + Template-ID: test-index + Generates: ai-docs/TEST_INDEX.md + Description: Repo-wide test surface — tiers, commands (by role), directories, frameworks, and coverage gate — routing to where cases live. + Library ver: 0.2.2 + Last updated: 2026-07-22 +─────────────────────────────── --> + +# Test Index — react-widgets + +> Start here → root [`AGENTS.md`](../AGENTS.md) (agent entry) · router [`SPEC_INDEX.md`](SPEC_INDEX.md) · system [`ARCHITECTURE.md`](ARCHITECTURE.md). This doc is the repo-wide map of the test surface. +> Context-efficiency: this is an INDEX, not a case list. It links to where cases live — it does not duplicate them. + +## Test Surface + +| Tier | Command (role) | Test directory | Framework | External deps | +|---|---|---|---|---| +| Unit | `npm run jest` | `packages/node_modules/**` (Jest remap to package `src/`) | Jest 24 | none | +| Lint / static analysis | `npm run static-analysis` | repository source | ESLint | none | +| Browser smoke | `npm run test:automation:smoke` | `test/journeys/specs/smoke/` | WebdriverIO 7 | built widget assets; local Selenium or Sauce | +| Space journeys | `npm run test:automation:space` | `test/journeys/specs/space/` | WebdriverIO 7 | Webex test users; media permissions | +| Recents journeys | `npm run test:automation:recents` | `test/journeys/specs/recents/` | WebdriverIO 7 | Webex test users; Mercury | +| TAP / integration | `npm run test:tap`, `npm run test:integration` | `test/journeys/` | WebdriverIO 7 | Sauce Labs / production-like env when `SAUCE=true` or `INTEGRATION=true` | + +## Where the Cases Live + +- **Unit test cases** → each module's spec, **Test-Case Strategy (module)** section (see [`SPEC_INDEX.md`](SPEC_INDEX.md) module registry). +- **Journey / smoke / TAP / accessibility cases** → [`modules/test-automation-spec.md`](modules/test-automation-spec.md) plus the protected source `test/journeys/testplan.md` (reconciled, not rewritten). +- **Per-feature system cases** → no `features/<KEY>/test-strategy.md` artifacts exist in this bootstrap; add them when a feature delta is specified. + +## Coverage / Quality Gate + +- Minimum: ESLint must pass (`npm run static-analysis`) · Measures: lint + Jest as `npm test` / husky pre-push · Applies to: whole repository source · Enforced in: CircleCI and local pre-push. +- Jest coverage reports may be generated locally but are not an enforced numeric gate in this brownfield repo. +- Browser journeys are environment-dependent and are not required for docs-only changes. + +## QA Dependencies & Environments + +- Journey suites need built static assets, Webex integration credentials, and sometimes Sauce Connect (`SAUCE=true`). +- Unit tests remap `@webex/*` to package `src/` and do not require a live SDK. +- Manual/QA cases are not tracked in a separate QA project; journey intent lives in `test/journeys/testplan.md`. + +## Where to Go Next + +- Agent entry: [`AGENTS.md`](../AGENTS.md) · System shape: [`ARCHITECTURE.md`](ARCHITECTURE.md) · Routing: [`SPEC_INDEX.md`](SPEC_INDEX.md) +- Machine source of truth: `.sdd/manifest.json` (`commands`). +- Detailed journey topology: [`modules/test-automation-spec.md`](modules/test-automation-spec.md) + +--- + +Provenance: generated_by `codex-desktop`; approved_by `pending PR approval`; updated_at `2026-09-03`. diff --git a/ai-docs/adr/0001-centralize-capability-sdd-docs.md b/ai-docs/adr/0001-centralize-capability-sdd-docs.md new file mode 100644 index 000000000..2fbd60dd4 --- /dev/null +++ b/ai-docs/adr/0001-centralize-capability-sdd-docs.md @@ -0,0 +1,46 @@ +<!-- ─────────────────────────────── + Template: ADR (example) + Template-ID: adr + Generates: ai-docs/adr/0001-centralize-capability-sdd-docs.md + Description: Standing architecture decision record — context, decision, alternatives rejected, consequences. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# ADR-0001 — Centralize capability-grouped SDD documentation + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +| Field | Value | +|---|---| +| Status | Accepted | +| Date | 2026-07-22 | +| Deciders | repository owner | +| Supersedes / Superseded by | none | +| Generated from | `adr` @ SDLC template library `0.2.2` | + +## Context + +The repository contains 103 package roots under one Git/build boundary. There was no canonical `ai-docs/` tree, and package-by-package documentation would fragment behaviors that span widgets, components, containers, reducers, SDK adapters, tooling, and tests. Evidence: `package.json`, `packages/node_modules/@webex/`, `scripts/`, `test/journeys/`. + +## Decision + +Keep one root `AGENTS.md`, centralized standing docs under `ai-docs/`, and ten capability-grouped canonical specs under `ai-docs/modules/`. `.sdd/manifest.json` is the machine router and `ai-docs/SPEC_INDEX.md` is its human mirror. + +## Alternatives Considered + +| Alternative | Pros | Cons | Why rejected | +|---|---|---|---| +| One spec per package | Precise source locality | More than 100 specs; cross-package widget flows become fragmented and costly to load/update | Does not match the approved maintenance capabilities | +| Source-local capability docs | Docs near source | Capability groups span many sibling package roots; no single honest source directory owns each group | Central routing is clearer for this package forest | +| Standing docs only | Small initial footprint | Omits module flows, requirements, errors, tests, and public-surface detail | Fails rigorous SDD and coverage goals | + +## Consequences + +- **Positive:** future agents can load one capability spec plus focused standing docs; cross-package contracts have one owner. +- **Negative / cost:** module specs must maintain package lists and cannot rely on directory locality for discovery. +- **Agents must:** update the capability spec, indexes/contracts, and manifest together when package ownership or public behavior changes. + +## Revisit When + +- The repository splits into independent Git/build/release units, or a capability grows large enough to require an approved module-map change. diff --git a/ai-docs/adr/0002-reconcile-existing-documentation.md b/ai-docs/adr/0002-reconcile-existing-documentation.md new file mode 100644 index 000000000..cf3dd9034 --- /dev/null +++ b/ai-docs/adr/0002-reconcile-existing-documentation.md @@ -0,0 +1,46 @@ +<!-- ─────────────────────────────── + Template: ADR (example) + Template-ID: adr + Generates: ai-docs/adr/0002-reconcile-existing-documentation.md + Description: Standing architecture decision record — context, decision, alternatives rejected, consequences. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# ADR-0002 — Reconcile existing documentation without replacement + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md) · [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +| Field | Value | +|---|---| +| Status | Accepted | +| Date | 2026-07-22 | +| Deciders | repository owner | +| Supersedes / Superseded by | none | +| Generated from | `adr` @ SDLC template library `0.2.2` | + +## Context + +The root README, package READMEs, widget event guides, and journey test plan contain install, compatibility, event, and test intent, but some statements predate current packages/constants. Replacing or deleting them would discard review history; treating all of them as current would propagate drift. + +## Decision + +Preserve protected source documents unchanged. Reorganize supported meaning into canonical SDD sections, use current source/tests to resolve conflicts, and keep unit-level disposition in ignored source-fidelity inventories. Canonical specs do not embed full-file snapshots. Reviewable unit counts and gate outcomes live in [`SDD_BOOTSTRAP_EVIDENCE.md`](../SDD_BOOTSTRAP_EVIDENCE.md). + +## Alternatives Considered + +| Alternative | Pros | Cons | Why rejected | +|---|---|---|---| +| Keep sources separate only | No migration effort | Agents still lack a canonical, code-checked route | Does not meet root-level SDD goal | +| Move/delete old docs | One apparent source | Loses history and breaks existing links | Violates the explicit preservation requirement | +| Copy old docs verbatim into `ai-docs/` | Fast | Reproduces stale statements and wrong section shape | Fails source-fidelity and conformance rules | + +## Consequences + +- **Positive:** reviewed source intent remains available while canonical docs describe current behavior. +- **Negative / cost:** future edits to protected source material require another fidelity/reconciliation pass. +- **Agents must:** never overwrite protected sources as a shortcut; resolve contradictions explicitly and cite stable code/test paths. Use [`SDD_BOOTSTRAP_EVIDENCE.md`](../SDD_BOOTSTRAP_EVIDENCE.md) for the committed reconcile summary; do not treat gitignored `.generated/` paths as PR evidence. + +## Revisit When + +- A human owner explicitly retires a protected document and approves its link/consumer migration. diff --git a/ai-docs/adr/README.md b/ai-docs/adr/README.md new file mode 100644 index 000000000..8edcd93e6 --- /dev/null +++ b/ai-docs/adr/README.md @@ -0,0 +1,12 @@ +# Architecture Decision Records + +Durable, append-only decisions for react-widgets. Accepted ADRs are superseded by a new numbered ADR rather than edited to reverse their meaning. + +## Use ADRs For + +- Understanding why centralized capability documentation and reconcile policy were selected. +- Recording a future durable architecture/process decision with alternatives and consequences. +- Knowing when a prior decision should be revisited without rewriting its history. + +- `0001-centralize-capability-sdd-docs.md` — central root documentation with capability-grouped module specs. +- `0002-reconcile-existing-documentation.md` — preserve legacy docs and reconcile them against current code/tests. diff --git a/ai-docs/modules/build-release-tooling-spec.md b/ai-docs/modules/build-release-tooling-spec.md new file mode 100644 index 000000000..1282cfd5a --- /dev/null +++ b/ai-docs/modules/build-release-tooling-spec.md @@ -0,0 +1,255 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/build-release-tooling-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Build and Release Tooling — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `build-release-tooling` | +| Source path(s) | `scripts/`, root build configs, `.circleci/config.yml`, package metadata | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 95% assessed 2026-07-22; commands, selection rules, outputs, CI promotion, and failure gates covered | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Root scripts, yargs command handlers, package metadata, bundler/transpiler configuration, lockfile, and CircleCI are authoritative. `scripts/release.sh` explicitly says it is not currently used and is historical, not the current delivery contract. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Repository usage guide development commands | local build/test | verified | Public Surface and Use Cases. | +| `package.json` scripts | command API | authoritative | Public Surface and Requirements. | +| build/publish scripts and CI | artifacts/release | authoritative | Design, rules, and failure handling. | +| Historical release helper | historical release approach | reference-only | Pitfalls; do not execute as the current workflow. | + +## Overview + +Root yargs CLIs discover packages beneath `packages/node_modules`, transpile package sources, build widget bundles, generate journey distributions and SRI metadata, start local demos/samples, and publish eligible packages. CircleCI installs with Node 22.22, then runs lint/Jest, browser journeys, and version/publish as sibling jobs; publish does not wait for the test jobs. + +## Purpose / Responsibility + +Turn the single Git repository into consistent per-package artifacts and enforce quality/promotion rules without changing package source semantics. + +## Stack + +Node.js 22.22 in CI, npm 10, Babel, Webpack 4, Rollup 2, Sass/PostCSS, yargs, SRI signing, standard-version, npm publishing, CircleCI, AWS S3/CloudFront, Netlify tooling, Jest, and WebdriverIO. + +## Folder / Package Structure + +```text +package.json, package-lock.json +babel.config.js, webpack.config.babel.js, rollup*.js +scripts/ +├── build/commands/ # all, components, dist, esm, journey, package.json, sri, transpile, widgets +├── start/commands/ # demo, package, samples +├── publish/commands/ # components, package +├── deploy/commands/ +├── tooling/commands/ +└── utils/ # build, package discovery, dependencies, publish, SRI, process helpers +.circleci/config.yml +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `package.json` | supported npm command surface | +| `scripts/build/index.js` and `commands/*.js` | build targets and selection | +| `scripts/utils/build.js` | Babel/Webpack/Rollup execution | +| `scripts/utils/package.js` | package discovery/classification | +| `scripts/publish/commands/*.js` | publish eligibility and dispatch | +| `.circleci/config.yml` | install, test, build, version, publish, CDN deployment | +| `rollup.config.js`, `rollup.calling-config.js`, `scripts/webpack/*` | artifact configuration | + +## Public Surface + +Repository command contracts: + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.cmd.install` | CLI | `npm install --legacy-peer-deps` | CI dependency installation | lockfile/CI contract | `.circleci/config.yml` | `../CONTRACTS.md` | +| `rw.cmd.build` | CLI | `npm run build {target}` plus root build aliases | package/widget artifacts and integrity | preserve yargs target names/arguments | `package.json`, `scripts/build/index.js`, `scripts/build/commands/` | `../CONTRACTS.md` | +| `rw.cmd.build-journey` | CLI | `npm run build journey {path}` | static browser-test assets | target path is destructive/generated | `scripts/build/commands/journey.js` | `../CONTRACTS.md` | +| `rw.cmd.serve` | CLI | `npm run serve {target}` / `npm start` | local development hosts | development only | `package.json`, `scripts/start/` | `../CONTRACTS.md` | +| `rw.cmd.publish` | CLI | `npm run publish:components` | publish eligible non-private packages | credentialed; never a casual verification command | `scripts/publish/commands/components.js`, `package.json` | `../CONTRACTS.md` | +| `rw.cmd.release` | CLI | `npm run release` | update version/changelog through standard-version | credentialed/release-only | `package.json` | `../CONTRACTS.md` | + +Compatibility notes: + +Command names and required arguments are automation interfaces. Build output is generated and excluded from source-of-truth documentation. + +## Requires (dependencies) + +Locked npm dependencies, a supported Node/npm runtime, package `name`/`private` metadata, filesystem/process access, CI credentials for npm/AWS/upstream Git, and a private key plus `widget-key.pub` for SRI. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `BUILD-R-001` | Package discovery and classification use package metadata; private packages are omitted from public ES/component publishing and demos are excluded from component publication. | Prevent accidental release of internal/example code. | `scripts/utils/package.js`, `scripts/build/commands/components.js`, `scripts/publish/commands/components.js` | `scripts/tooling/commands/check-testable.js`, `.circleci/config.yml` | No dedicated publish dry-run suite found. | PRESENT | +| `BUILD-R-002` | Public widgets receive distributable bundles while packages receive transpiled/module outputs appropriate to the selected command. | Consumers use both embeddable bundles and package imports. | `scripts/build/commands/all.js`, `scripts/build/commands/widgets.js`, `scripts/utils/build.js` | `.circleci/config.yml` | Artifact snapshots are limited. | PRESENT | +| `BUILD-R-003` | Journey builds copy static servers/axe and build Space, Recents, Demo, and current calling distributions into the requested target. | Browser tests require a self-contained served tree. | `scripts/build/commands/journey.js` | `.circleci/config.yml` | Some calling output paths share `dist-call-history`; validate before altering. | PRESENT | +| `BUILD-R-004` | SRI generation fails safely when `PRIVATE_KEY` is absent and signs package distributions with the repository public key when present. | CDN consumers need verifiable assets without exposing signing material. | `scripts/build/commands/sri.js`, `scripts/utils/sri.js` | `.circleci/config.yml` | Secret provisioning is external. | PRESENT | +| `BUILD-R-005` | CI starts `version_and_publish` after `install` only. Lint/Jest (`unit_tests_and_linting`) and Chrome/Firefox journeys are sibling jobs and do not gate version/publish. The master `build_for_cdn` workflow is `install` → `version_and_publish` → `deploy_to_cdn` with the same install-only publish dependency. | Hosts and operators need the actual workflow dependencies, not an assumed test gate. | `.circleci/config.yml` | CI workflow itself | Lint/Jest and journeys can fail independently of publish; external services/credentials can still block a job. | PRESENT | +| `BUILD-R-006` | Version/publish changes preserve package dependency/version consistency and publish only non-private eligible packages. | The repository releases many interdependent packages from one versioned source. | `scripts/utils/deps.js`, `scripts/publish/commands/components.js`, `.circleci/config.yml` | `package.json` | Rollback is operational/manual. | PRESENT | + +## Design Overview + +Root npm aliases dispatch to small yargs command handlers. Package utilities enumerate the nonstandard `packages/node_modules` tree; build helpers centralize Babel, Rollup, and Webpack invocation. CI persists dependencies and built journey assets between jobs. `version_and_publish` requires only `install`; lint/Jest and journeys run in parallel and do not gate publish or CDN deploy. + +## Data Flow + +```mermaid +flowchart LR + Source[Package source + metadata] --> Discover[Package discovery] + Discover --> Transpile[Babel CJS/ES] + Discover --> Bundle[Webpack/Rollup bundles] + Bundle --> SRI[SRI metadata] + Transpile --> NPM[npm packages] + Bundle --> Journey[Journey static tree] + Journey --> BrowserTests[WDIO] + NPM --> Publish[npm publish] + SRI --> CDN[S3/CloudFront] +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| package artifact build | Artifact build | invalid target/build failure stops artifact production | +| CI verify, version, publish, deploy | Parallel promotion | install failure prevents publish; test-job failures do not; partial external failure requires operator review | + +```mermaid +sequenceDiagram + participant D as Developer/CI + participant Y as Build yargs command + participant P as Package discovery + participant B as Babel/Webpack/Rollup + D->>Y: target + package/path + Y->>P: enumerate and filter metadata + P-->>Y: eligible package paths + loop each selected package + Y->>B: transpile/bundle + alt build succeeds + B-->>D: generated artifact + else invalid target or compiler failure + B-->>D: nonzero/throw; no promotion + end + end +``` + +```mermaid +sequenceDiagram + participant C as CircleCI + participant N as npm/build CLIs + participant T as Tests + participant R as Registries/CDN + C->>N: install locked dependencies + par after install + C->>T: lint + Jest + C->>N: build journey assets then Chrome/Firefox suites + C->>N: version + production builds + SRI + end + alt version_and_publish succeeds + C->>R: push upstream/tag, publish npm; master CDN deploy follows publish + else install or publish job fails + C-->>R: no promotion from that failed job + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class NpmScripts + class YargsCommands + class PackageDiscovery + class BuildHelpers + class CircleCI + class ArtifactTargets + NpmScripts --> YargsCommands + YargsCommands --> PackageDiscovery + YargsCommands --> BuildHelpers + CircleCI --> NpmScripts + BuildHelpers --> ArtifactTargets +``` + +## Use Cases + +- Build every public widget and transpile every package for a release candidate. +- Build one package during focused development. +- Generate the static journey tree consumed locally or in CI. +- Publish all eligible component/module packages while skipping private/demo packages. +- Produce and deploy versioned Space/Recents/Demo CDN assets with SRI. + +## Business Rules & Invariants + +- `private: true` packages and demo packages are never published by the bulk component command. +- Build output is generated; source remains under package `src`. +- Release promotion uses the installed commit/version and requires credentials from CI, never committed files. Lint/Jest and journeys are not `requires` of `version_and_publish`. +- SRI private keys come only from the environment. + +## Concurrency & Reactive Flow + +CI jobs share cached dependencies and persisted journey artifacts. Package loops may invoke builds sequentially but external publish/deploy operations have partial-failure risk; reruns must not silently publish a different source commit under the same version. + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| build/transpile failure | command throws/non-zero process | fix source/config and rerun from clean output | +| command argument missing | yargs usage error or false result | supply documented target/package | +| SRI key missing | logged error and false result | provision `PRIVATE_KEY` through approved secret store | +| registry/CDN/Git failure | CI job failure or partial publish | inspect versions/artifacts before controlled retry | + +## Pitfalls + +- `scripts/release.sh` deletes/rebuilds large trees and declares itself unused; do not run it as the current release procedure. +- The repository layout resembles installed `node_modules` but is tracked source. +- `build journey` contains repeated calling-output destinations and asynchronous copy/build sequencing that merits characterization before refactoring. +- Bulk publish currently catches errors; review logs to detect partial publication. + +## Module Do's / Don'ts + +- Do preserve private/demo filters and verify artifacts from a clean checkout. +- Do keep secrets in environment/CI stores. +- Don't publish or deploy from an unvalidated local worktree. +- Don't change package discovery or artifact paths without exercising every build target. + +## Key Design Trade-off + +Central scripts give 100+ packages one consistent release pipeline, but the custom tracked-node_modules layout and mixed Babel/Webpack/Rollup outputs make broad tooling changes high blast radius. + +## Test-Case Strategy (module) + +| Requirement | Current evidence | Focused gap | +|---|---|---| +| `BUILD-R-001` filters | `scripts/build/commands/components.js`, `scripts/publish/commands/components.js` | dry-run eligibility snapshot | +| `BUILD-R-002` artifacts | `scripts/build/commands/all.js`, `.circleci/config.yml` | clean artifact manifest | +| `BUILD-R-003` journey tree | `scripts/build/commands/journey.js`, `.circleci/config.yml` | calling destination assertions | +| `BUILD-R-004` SRI | `scripts/build/commands/sri.js`, `.circleci/config.yml` | missing/malformed key tests | +| `BUILD-R-005` gates | `.circleci/config.yml` | local workflow lint/schema check | +| `BUILD-R-006` version/publish | `scripts/publish/commands/components.js`, `.circleci/config.yml` | partial publish recovery runbook | + +## Traceability + +- Commands and external boundaries: `../GETTING_STARTED.md`, `../CONTRACTS.md`. +- Security/review rules: `../SECURITY.md`, `../REVIEW_CHECKLIST.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/calling-spec.md b/ai-docs/modules/calling-spec.md new file mode 100644 index 000000000..2153650ba --- /dev/null +++ b/ai-docs/modules/calling-spec.md @@ -0,0 +1,327 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/calling-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Calling Widgets — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `calling` | +| Source path(s) | `packages/node_modules/@webex/widget-call-history/`, `widget-number-pad/`, `widget-speed-dial/`, `widget-voice-mail/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 91% assessed 2026-07-22; all typed entrypoints, adapter/call/audio/form flows, UI state and risks covered; automated tests are sparse outside GenericModal | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +TypeScript entrypoints/interfaces, adapter contexts/hooks, component implementations, stories, and the one current Jest test are evidence. Placeholder package READMEs do not establish behavior beyond package identity/import examples. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Call History and Speed Dial placeholder READMEs | package identity/import | verified/unsupported | Valid package identity is in Public Surface; empty install sections and generic summaries are unsupported by code and not promoted. | +| Generic Modal README | component import | verified | The current internal modal relationship is documented in Public Surface and UI Flow. | +| Stories and TypeScript types | API/examples | used/reference-only | Exact props remain native type/story detail; stable exported concepts are summarized below. | + +## Overview + +Calling is a newer TypeScript/React component family separate from the legacy enhanced widgets. Call History renders call records and call-back controls; Number Pad handles digits, long-press behavior, contact/call selection, and keyboard focus; Speed Dial renders/sorts/creates/edits/removes callable records; Voice Mail renders messages and accessible audio playback/scrubbing. + +Packages use Webex component-adapter interfaces and React contexts/hooks to make calls or fetch/search data without constructing the legacy Redux/Webex runtime. Consumers supply typed data, callbacks, adapters, translation providers, and styles. + +## Purpose / Responsibility + +Own reusable calling-oriented UI and interaction state for history, dialing, speed dials, and voicemail. It does not own calling service persistence, call signaling, contact storage, or voicemail storage. + +## Stack + +TypeScript 4.5, React hooks/contexts, Momentum UI collaboration components, Webex component adapter interfaces, react-i18next, react-hook-form, react-stately/React Aria utilities, Sass, Storybook, and limited Jest. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── widget-call-history/src/ # list/item types, call hook, date/voiceover helpers +├── widget-number-pad/src/ # dial pad, call buttons, contact search/popover, focus hooks +├── widget-speed-dial/src/ # list/items/forms/search/modals/photo/banner and types +└── widget-voice-mail/src/ # voicemail item, playback controls, scrubbing/audio hooks +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/widget-call-history/src/index.ts`, `packages/node_modules/@webex/widget-number-pad/src/index.ts`, `packages/node_modules/@webex/widget-speed-dial/src/index.ts`, `packages/node_modules/@webex/widget-voice-mail/src/index.ts` | public exports | +| `packages/node_modules/@webex/widget-call-history/src/CallHistoryItem.types.ts` | call-history record contract | +| `packages/node_modules/@webex/widget-speed-dial/src/SpeedDials.types.tsx` | speed-dial records/events/list props | +| `packages/node_modules/@webex/widget-speed-dial/src/SpeedDialForm.types.tsx` | create/edit form types | +| package `src/contexts/AdapterContext.tsx` | supplied adapter contracts | +| `packages/node_modules/@webex/widget-number-pad/src/utils/WebexDialPad.ts` | long-press/focus constants | +| `packages/node_modules/@webex/widget-voice-mail/src/hooks/useAudio.ts` | audio playback state/listener lifecycle | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.widget.call-history` | React/TypeScript | `CallHistoryItem`, `CallHistory`, `NoHistory`, item types | render/select/call back history records | public semver/type surface | `packages/node_modules/@webex/widget-call-history/src/index.ts` | `../CONTRACTS.md` | +| `rw.widget.number-pad` | React/TypeScript | `NumberPad`, `CallButtons`, contact/search/popover components | digit entry, calling controls, contact selection | public semver/type surface | `packages/node_modules/@webex/widget-number-pad/src/index.ts` | `../CONTRACTS.md` | +| `rw.widget.speed-dial` | React/TypeScript | `SpeedDials`, item/form/search/banner + form/list types | manage and activate speed-dial records | public semver/type surface | `packages/node_modules/@webex/widget-speed-dial/src/index.ts` | `../CONTRACTS.md` | +| `rw.widget.voice-mail` | React/TypeScript | `VoicemailItem`, playback controls, scrubbing bar | render/play/seek/call back voicemail | public semver/type surface | `packages/node_modules/@webex/widget-voice-mail/src/index.ts` | `../CONTRACTS.md` | + +Compatibility notes: + +- Callback names, record fields, optional/default behavior, focus/keyboard semantics, and exported TypeScript types are public. +- Internal hooks/contexts not re-exported at package `index.ts` are implementation surfaces unless a consumer deep-import already exists. + +## Requires (dependencies) + +- React host plus translation/theme/style providers expected by components. +- `@webex/component-adapter-interfaces`, `@webex/sdk-component-adapter`, Momentum UI/Webex Components. +- Host-supplied record arrays, callbacks/adapters, audio source URLs, refs, and permissions. +- Browser Audio, localStorage, MutationObserver, keyboard/focus, and file/image APIs for applicable controls. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `CALLING-R-001` | Call History renders supplied records in order, preserves selected state, forwards row/call actions, and can dismiss a badge on row activation. | The package is a controlled list; hosts own records and follow-up call behavior. | `packages/node_modules/@webex/widget-call-history/src/CallHistory.tsx`, `packages/node_modules/@webex/widget-call-history/src/CallHistoryItem.types.ts` | `packages/node_modules/@webex/widget-call-history/src/CallHistory.stories.tsx` | No Jest test found. | PRESENT | +| `CALLING-R-002` | Number Pad reports button values, supports configured long-press alternatives, keyboard grid focus, and contact/call selection callbacks. | Dial input and keyboard accessibility must be deterministic. | `packages/node_modules/@webex/widget-number-pad/src/NumberPad.tsx`, `packages/node_modules/@webex/widget-number-pad/src/hooks/useGridFocus.ts`, `packages/node_modules/@webex/widget-number-pad/src/utils/WebexDialPad.ts` | `packages/node_modules/@webex/widget-number-pad/src/NumberPad.stories.tsx` | No Jest test found. | PRESENT | +| `CALLING-R-003` | Speed Dial renders records and forwards press/audio/video/edit/remove/add/sort callbacks with typed record shapes. | Hosts/adapters own persistence and calls; UI must not invent side effects. | `packages/node_modules/@webex/widget-speed-dial/src/SpeedDials.tsx`, `packages/node_modules/@webex/widget-speed-dial/src/SpeedDials.types.tsx` | `packages/node_modules/@webex/widget-speed-dial/src/SpeedDialModal.stories.tsx` | Core list/form tests sparse. | PRESENT | +| `CALLING-R-004` | Speed Dial forms validate/create/edit inputs and keep modal/search/photo/error UI state consistent on cancel/submit/failure. | Invalid or stuck form state creates bad callable records and inaccessible modal flow. | `packages/node_modules/@webex/widget-speed-dial/src/SpeedDialForm.tsx`, `packages/node_modules/@webex/widget-speed-dial/src/SpeedDialForm.types.tsx`, `packages/node_modules/@webex/widget-speed-dial/src/GenericModal/GenericModal.tsx` | `packages/node_modules/@webex/widget-speed-dial/src/GenericModal/GenericModal.test.tsx` | Full validation matrix not covered. | WEAK | +| `CALLING-R-005` | Voicemail playback loads audio, reports play state/time/duration, supports seek and focus transitions, and removes audio listeners on cleanup. | Audio controls must remain synchronized and accessible across repeated items/mounts. | `packages/node_modules/@webex/widget-voice-mail/src/VoicemailPlaybackControls.tsx`, `packages/node_modules/@webex/widget-voice-mail/src/hooks/useAudio.ts` | `packages/node_modules/@webex/widget-voice-mail/src/ScrubbingBar.stories.tsx` | No Jest test found. | PRESENT | +| `CALLING-R-006` | Adapter-backed call/search operations handle missing adapters and rejected promises without committing service state locally. | These packages are UI adapters, not service owners; failures must remain recoverable by the host. | `packages/node_modules/@webex/widget-call-history/src/contexts/AdapterContext.tsx`, `packages/node_modules/@webex/widget-call-history/src/hooks/useMakeCall.ts`, `packages/node_modules/@webex/widget-speed-dial/src/hooks/useContactSearch.ts` | `packages/node_modules/@webex/widget-call-history/src/CallHistory.stories.tsx` | Error contract needs stronger tests. | WEAK | + +## Design Overview + +The packages are controlled component layers. Record arrays and callbacks are supplied by consumers, while small hooks provide local focus, debounce, form, audio, or adapter-call state. Adapter contexts decouple UI from a specific SDK instance and allow a host to inject the component-adapter implementation. + +The shared `useWebexClasses` pattern creates predictable BEM-like classes, and Storybook stories serve as executable usage examples. TypeScript entrypoints intentionally export selected components/types rather than every internal modal/hook. + +## Data Flow + +```mermaid +flowchart LR + Host[Records, callbacks, adapters, translations] --> Components[Calling React components] + Components --> Local[Hook/form/focus/audio state] + Components --> Adapter[Context adapter or host callback] + Adapter --> Services[Webex calling/contact/voicemail services] + Services --> Host + Host --> Components + Components --> Browser[Audio, keyboard, focus, localStorage, observer] +``` + +Remote operations use host callbacks or adapter promises; component composition is in-process React; audio/focus interactions use browser APIs. + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| select/call record or speed dial | Controlled action | missing callback/adapter and rejection | +| create/edit speed dial | Form/modal flow | validation/cancel/service failure | +| play/seek voicemail | Audio flow | load/end/unmount recovery | + +```mermaid +sequenceDiagram + participant U as User + participant C as History/Dial/SpeedDial UI + participant H as Host callback or adapter + participant S as Calling service + U->>C: select or press audio/video call + alt callback/adapter available + C->>H: record/address + call mode/label + H->>S: make call + alt rejected + S-->>H: error + H-->>C: recoverable error state + else accepted + S-->>H: call result + end + else unavailable + C-->>U: no service side effect; host must configure adapter + end +``` + +```mermaid +sequenceDiagram + participant U as User + participant M as SpeedDial modal + participant F as Form + participant H as Host/adapter + U->>M: add or edit + M->>F: initialize values + alt invalid + F-->>U: validation errors; submit disabled + else valid submit + F->>H: create/update record + alt failure + H-->>M: error modal/banner; form retained + else success + H-->>M: close and host refreshes items + end + end + opt cancel + U->>M: cancel; discard local edits + end +``` + +```mermaid +sequenceDiagram + participant U as User + participant V as Voicemail controls + participant A as HTMLAudioElement + U->>V: play + V->>A: play() + A-->>V: time/duration/end events + U->>V: seek or pause + V->>A: set currentTime or pause() + alt audio ends + A-->>V: playing=false + focus update + else component unmounts + V->>A: remove listeners/pause cleanup + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class AdapterProvider + class CallHistory + class NumberPad + class SpeedDials + class SpeedDialForm + class VoicemailItem + class VoicemailPlaybackControls + class ScrubbingBar + AdapterProvider --> CallHistory + AdapterProvider --> SpeedDials + AdapterProvider --> VoicemailItem + SpeedDials --> SpeedDialForm + VoicemailItem *-- VoicemailPlaybackControls + VoicemailPlaybackControls *-- ScrubbingBar +``` + +## Use Cases + +- **UC-1 Review and return a call:** host supplies history → user selects/calls an item → callback/adapter initiates call → host updates records/selection. +- **UC-2 Dial a number/contact:** user enters digits or searches contacts → keyboard/long-press rules update input → call selection invokes supplied action. +- **UC-3 Manage speed dials:** user adds/edits/removes/reorders records → validated callback/adapter request → host refreshes controlled list. +- **UC-4 Play/call back voicemail:** user plays/seeks/pauses an audio source or presses call-back → audio state/focus updates and supplied call action runs. +- **UI flow:** list/empty/error → selected item/action controls; Speed Dial additionally uses add/edit/search/photo/modal/error screens; Voice Mail uses play/pause/scrub/focus states. +- **Cross-service flow:** adapters/callbacks cross to Webex component services; all persisted results return from the host rather than being owned in component state. + +## State Model + +- Controlled data: call history items, selected record, speed-dial records, voicemail metadata, and host callbacks/adapters. +- Local state: focus/keyboard indices, digit press timing, search/debounce, form validity/dirty state, modal selection/error, image preview, audio object/current time/duration/playing. +- Browser focus flags are temporary and removed on blur/unmount; they are not domain persistence. + +## Business Rules & Invariants + +- Stable item IDs drive React keys/selection/reordering; do not use array position as durable identity. +- Speed Dial submit remains disabled when invalid and, for edits, when not dirty. +- Long-press behavior applies only to configured dial-pad values and must not also emit the short value after the threshold. +- Audio play/pause/time and focus state must reflect the same `HTMLAudioElement` lifecycle. + +## Concurrency & Reactive Flow + +- Debounced search and adapter calls can resolve out of order; callers/hooks must avoid applying stale results. +- Audio time/end events update React state asynchronously and must remove listeners on cleanup. +- Timers implement dial-pad long press; cleanup must prevent delayed callbacks after unmount/release. +- MutationObserver/focus listeners in call-selection UI require symmetric removal. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Idle + Idle --> Editing: add/edit speed dial + Editing --> Invalid: validation fails + Invalid --> Editing: input corrected + Editing --> Saving: valid submit + Saving --> Idle: host confirms and refreshes + Saving --> Error: adapter rejects + Error --> Editing: retry + Idle --> Playing: play voicemail + Playing --> Paused: pause/seek + Paused --> Playing: resume + Playing --> Idle: ended/unmounted +``` + +## UI Flow + +- Call History: list/empty → row selected → audio/video/callback action. +- Number Pad: focus grid → digit/long press → optional contact results → call-mode selection. +- Speed Dial: list/error/empty → add/edit modal → validated form/search/photo → save/cancel/error → reordered list. +- Voice Mail: item → playback controls → scrub/focus/ended states, with call-back action when provided. +- Preserve labels, focus visibility, arrow-key transitions, list position announcements, and German/locale-specific formatting flags. + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| no callback/adapter function | optional call is not performed | provide the required callback/AdapterProvider | +| adapter/search/save rejects | hook/form error or host rejection | retain input, show error/banner/modal, retry/cancel | +| invalid speed-dial input | form errors/disabled submit | correct required fields/type/addresses | +| audio fails to load/play | missing duration/play rejection | show non-playing state and provide valid source/browser permission | +| empty record list | empty/list UI | host supplies records or user adds when supported | +| component unmount with active timer/listener/audio | leak/stale callback risk | cleanup timer/listener/observer/audio in effect return | + +## Pitfalls + +- Package READMEs are placeholder-level and contain empty install commands; use entrypoints/types/stories, not those blanks. +- Several packages deep-import adapter interface distribution paths (`dist/cjs` or `dist/esm`); dependency upgrades can break type/runtime resolution. +- `dismissBagdeonClickRow` is misspelled in the public Call History type/implementation; correcting spelling alone would be breaking. +- Number Pad uses localStorage for focus coordination, not persisted dialing data; always remove temporary keys. +- Calling packages have far less Jest coverage than legacy component packages; characterize before behavior changes. + +## Module Do's / Don'ts + +- DO keep components controlled and forward service operations through typed callbacks/adapters. +- DO preserve focus cleanup and accessible announcements when changing markup. +- DON'T introduce hidden persistence, silently rename misspelled public props, or export internal hooks without a contract review. + +## Export Stability + +All symbols in the four `src/index.ts` files and their exported TypeScript interfaces are public semver surfaces. Additive optional props/types are preferred; removals/required-field changes need a major migration. The placeholder READMEs do not narrow these actual exports. + +## Host Integration & Theming + +Consumers mount React components, supply Momentum/Webex styling and i18n context, and inject data/callbacks or adapter providers. Components assume browser focus/audio/observer APIs where used but do not assume the legacy `window.webex.widget` runtime. + +## Key Design Trade-off + +- Controlled components and adapter contexts maximize host portability and keep service state outside the UI packages, at the cost of requiring consumers to wire records, persistence, translations, and failure handling explicitly. + +## Test-Case Strategy (module) + +Storybook stories cover visual/use-case examples; `GenericModal.test.tsx` covers the only discovered Jest target. Before modifying behavior, add React tests for each exported component/hook boundary with positive action and negative missing/rejected/cleanup cases, plus keyboard/focus/audio assertions. Adapter integration belongs in a host/component-adapter test layer. + +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| `CALLING-R-001` Call History | `packages/node_modules/@webex/widget-call-history/src/CallHistory.stories.tsx` | no Jest coverage | +| `CALLING-R-002` Number Pad | `packages/node_modules/@webex/widget-number-pad/src/NumberPad.stories.tsx` | long-press/grid/contact Jest coverage | +| `CALLING-R-003` Speed Dial callbacks/sort | `packages/node_modules/@webex/widget-speed-dial/src/SpeedDialModal.stories.tsx` | core list tests | +| `CALLING-R-004` form/modal | `packages/node_modules/@webex/widget-speed-dial/src/GenericModal/GenericModal.test.tsx` | validation/save/error matrix | +| `CALLING-R-005` voicemail audio | `packages/node_modules/@webex/widget-voice-mail/src/ScrubbingBar.stories.tsx` | audio event/cleanup/focus tests | +| `CALLING-R-006` adapters/errors | `packages/node_modules/@webex/widget-call-history/src/hooks/useMakeCall.ts` | missing/rejected adapter tests | + +## Traceability + +- Architecture: `../ARCHITECTURE.md`; registry: `../SPEC_INDEX.md`; contracts: `../CONTRACTS.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/containers-hooks-spec.md b/ai-docs/modules/containers-hooks-spec.md new file mode 100644 index 000000000..63bf688cd --- /dev/null +++ b/ai-docs/modules/containers-hooks-spec.md @@ -0,0 +1,256 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/containers-hooks-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Containers and HOCs — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `containers-hooks` | +| Source path(s) | `packages/node_modules/@webex/react-container-*/`, `react-hoc-*/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 92% assessed 2026-07-22; every package and lifecycle boundary covered, with gaps around repeated subscription cleanup | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Connected-container exports, selectors, actions/reducers, lifecycle methods, tests, and consuming widgets determine behavior. Despite the capability name, the repository currently implements class/connect HOCs rather than a general React Hooks layer. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Legacy container/HOC rename READMEs | namespace migration | verified | Export Stability records the suffix-preserving `@webex` mapping. | +| Container/HOC source | injection and lifecycle | authoritative | Public Surface through Concurrency. | +| Adjacent Jest tests | projections/interactions | verified | Test-Case Strategy. | + +## Overview + +Seven container packages connect shared UI to Redux/SDK-aware behavior: activity list, file downloader, message composer, notifications, presence avatar, read receipts, and scrolling activity. Two HOCs add conversation Mercury handling and imperative scroll behavior. + +## Purpose / Responsibility + +Translate application state and actions into presentational props, and own only the subscriptions or DOM capabilities necessary for that translation. Containers must preserve wrapped-component contracts and clean up resources they create. + +## Stack + +React 16, react-redux `connect`, Redux actions/reducers, Immutable.js, reselect, recompose conventions, PropTypes, Webex SDK/Mercury, CSS modules, and Jest. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── react-container-activity-list/src/ +├── react-container-file-downloader/src/ +├── react-container-message-composer/src/ +├── react-container-notifications/src/ +├── react-container-presence-avatar/src/ +├── react-container-read-receipts/src/ +├── react-container-scrolling-activity/src/ +├── react-hoc-conversation-mercury/src/ +└── react-hoc-scrollable/src/ +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/react-container-activity-list/src/index.js` and the exact container paths indexed in `../CONTRACTS.md` | public default/named exports | +| `packages/node_modules/@webex/react-container-activity-list/src/selectors.js` | activity projection | +| `packages/node_modules/@webex/react-container-message-composer/src/container.js` | composer state/actions | +| `packages/node_modules/@webex/react-container-notifications/src/container.js` | notification integration | +| `packages/node_modules/@webex/react-hoc-conversation-mercury/src/index.js` | conversation subscription/filtering | +| `packages/node_modules/@webex/react-hoc-scrollable/src/index.js` | imperative scrolling API | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.containers` | SDK/React | seven `react-container-*` default entrypoints | connect presentation to selected state/actions | public semver; injected/caller props remain compatible | exact `rw.container.*` catalog paths; representative: `packages/node_modules/@webex/react-container-activity-list/src/index.js`, `packages/node_modules/@webex/react-container-message-composer/src/index.js` | `../CONTRACTS.md` | +| `rw.hoc.conversation-mercury` | SDK/React | `wrapConversationMercury(Component)` | filter/dispatch live conversation activity | public semver; `WrappedComponent` and display name are observable | `packages/node_modules/@webex/react-hoc-conversation-mercury/src/index.js` | `../CONTRACTS.md` | +| `rw.hoc.scrollable` | SDK/React | `injectScrollable(Component)` plus imperative scroll methods | reusable activity scrolling | public semver; methods and thresholds are observable | `packages/node_modules/@webex/react-hoc-scrollable/src/index.js` | `../CONTRACTS.md` | + +Compatibility notes: + +Message composer and notifications also expose actions and a reducer. Injected props are implementation-owned; caller-facing props remain declared alongside each wrapper. + +## Requires (dependencies) + +Redux slices/actions/selectors, shared components, React/DOM lifecycle, Webex SDK/Mercury for live conversation behavior, and owning widgets to compose any exported reducers. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `CONT-R-001` | Containers derive presentational props without changing caller-supplied props except for documented injections. | Wrapped components must remain reusable and predictable. | `packages/node_modules/@webex/react-container-activity-list/src/index.js`, `packages/node_modules/@webex/react-container-activity-list/src/selectors.js` | `packages/node_modules/@webex/react-container-activity-list/src/index.test.js`, `packages/node_modules/@webex/react-container-activity-list/src/selectors.test.js` | Prop collision behavior is not uniformly tested. | PRESENT | +| `CONT-R-002` | Containers that export reducers/actions expose them through the package barrel for widget composition. | State behavior must be installable with the connected component. | `packages/node_modules/@webex/react-container-message-composer/src/index.js`, `packages/node_modules/@webex/react-container-notifications/src/index.js` | `packages/node_modules/@webex/react-container-message-composer/src/index.test.js`, `packages/node_modules/@webex/react-container-notifications/src/container.test.js` | None known. | PRESENT | +| `CONT-R-003` | Conversation Mercury handling subscribes only after connection and conversation identity exist and ignores other conversations/replies. | Prevent duplicate or cross-conversation activity updates. | `packages/node_modules/@webex/react-hoc-conversation-mercury/src/index.js` | adjacent HOC tests | Listener removal is not explicit in this HOC. | WEAK | +| `CONT-R-004` | Scrollable exposes stable threshold-based top/bottom methods around the wrapped content node. | Activity views need reusable infinite-scroll and jump-to-bottom behavior. | `packages/node_modules/@webex/react-hoc-scrollable/src/index.js` | adjacent HOC tests | Thresholds 100/150 pixels are current behavior. | PRESENT | +| `CONT-R-005` | File, composer, notification, presence, receipt, and activity projections surface SDK/action failures through owning state/callback paths. | Containers should not swallow failures between data and UI layers. | `packages/node_modules/@webex/react-container-file-downloader/src/index.js`, `packages/node_modules/@webex/react-container-message-composer/src/container.js`, `packages/node_modules/@webex/react-container-notifications/src/container.js` | `packages/node_modules/@webex/react-container-file-downloader/src/index.test.js`, `packages/node_modules/@webex/react-container-message-composer/src/index.test.js` | Error rendering may be owned by parent UI. | PRESENT | + +## Design Overview + +Containers are intentionally thin adapters. Selectors/projectors turn Immutable state into component-friendly data, dispatch bindings supply operations, and the wrapped component remains responsible for presentation. HOCs own cross-cutting lifecycle behavior that cannot be expressed as a simple projection. + +## Data Flow + +```mermaid +flowchart LR + Store[Redux store] --> Selector[mapState/selectors] + Parent[Caller props] --> Container[Connected container/HOC] + Selector --> Container + Container --> View[Shared component] + View -->|callback| Dispatch[bound action] + Dispatch --> Store + SDK[Mercury/SDK] --> HOC[Lifecycle subscription] + HOC --> Dispatch +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| project and dispatch | Container interaction | initial projection and action-result rerender | +| subscribe and filter realtime activity | Mercury conversation flow | other-conversation/reply ignore branch; teardown gap is recorded in Pitfalls | + +```mermaid +sequenceDiagram + participant W as Widget + participant C as Container + participant S as Store + participant V as View + W->>C: caller props + S-->>C: selected state + C->>V: merged presentation props + V-->>C: user callback + C->>S: dispatch action + S-->>V: rerendered projection +``` + +```mermaid +sequenceDiagram + participant H as Mercury HOC + participant M as Mercury + participant R as Conversation reducer + H->>M: subscribe after connected + conversation id + M-->>H: conversation.activity + alt matching conversation and supported object + H->>R: receive activity/comment or participant change + else other conversation or reply + H-->>H: ignore + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class PresentationalComponent + class ConnectedContainer + class ReduxStore + class ConversationMercuryHOC + class ScrollableHOC + ConnectedContainer --> PresentationalComponent + ConnectedContainer --> ReduxStore + ConversationMercuryHOC --> ConnectedContainer + ScrollableHOC --> PresentationalComponent +``` + +## Use Cases + +- Project normalized activities into an activity-list component. +- Submit composer text/files through bound operations and local reducer state. +- Receive a matching Mercury conversation event and dispatch the appropriate update. +- Download a file through SDK-backed state while reporting failure. +- Allow an activity surface to measure, set, and classify its scroll position. + +## State Model + +Most containers are projections with no independent state. Message composer, notifications, and presence-related packages may own reducer slices. HOCs keep only lifecycle/DOM references; the conversation HOC records `isListeningToMercury` in the conversation state to guard registration. + +## Business Rules & Invariants + +- Do not dispatch conversation activity for a different conversation. +- Reply activities are currently ignored by the Mercury HOC. +- Person-object add/leave verbs map to participant actions before the comment is recorded. +- Scroll top/bottom calculations use the wrapped node and current 100/150-pixel thresholds. + +## Concurrency & Reactive Flow + +Redux updates, prop changes, DOM scroll events, and Mercury events are independent. Subscription guards must be idempotent; unmount or conversation changes must not leave callbacks that target stale stores/components. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Waiting + Waiting --> Listening: Mercury connected + conversation id + Listening --> Listening: matching supported event + Listening --> Ignored: other conversation/reply + Ignored --> Listening + Listening --> Disposed: unmount/teardown +``` + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| selected state not loaded | initial/empty projection | render bounded empty/loading state | +| bound SDK/action rejects | owning reducer error or callback result | parent renders error/retry | +| scroll node absent | imperative method cannot complete | call only after mount; guard new call sites | +| duplicate/unremoved Mercury listener | duplicate dispatched activity | dispose listener and characterize reconnect behavior | + +## Pitfalls + +- `react-hoc-conversation-mercury` registers a listener but does not visibly remove it; characterize behavior before changing it. +- `shouldComponentUpdate(nextProps) { return nextProps !== this.props; }` relies on reference changes. +- “containers-hooks” is a capability label, not evidence that these packages export Hooks. + +## Module Do's / Don'ts + +- Do keep selectors deterministic and injected props explicit. +- Do add teardown for newly introduced listeners/timers. +- Don't move presentation decisions into a state connector. +- Don't change imperative scrolling methods without checking activity consumers. + +## Export Stability + +Container and HOC package entrypoints are public semver surfaces. Protected `@ciscospark` notices document the same package suffix under `@webex`; wrapper display names and `WrappedComponent` are observable in tests/tooling. + +## Host Integration & Theming + +Containers pass through host/widget props and render shared components, inheriting their CSS/intl/accessibility behavior. Browser-specific behavior is limited to DOM scrolling, downloads, notifications, and realtime SDK events. + +## Key Design Trade-off + +Thin packages keep concerns reusable, but lifecycle behavior spread across HOCs and widgets makes listener ownership harder to audit. New cross-cutting behavior should state exactly who registers and disposes it. + +## Test-Case Strategy (module) + +| Requirement | Current evidence | Focused gap | +|---|---|---| +| `CONT-R-001` projection | `packages/node_modules/@webex/react-container-activity-list/src/index.test.js`, `packages/node_modules/@webex/react-container-activity-list/src/selectors.test.js` | prop collision matrix | +| `CONT-R-002` exports | `packages/node_modules/@webex/react-container-message-composer/src/index.test.js` | public export snapshot | +| `CONT-R-003` Mercury filters | None found adjacent to `packages/node_modules/@webex/react-hoc-conversation-mercury/src/index.js` | teardown/reconnect characterization | +| `CONT-R-004` scrolling | None found adjacent to `packages/node_modules/@webex/react-hoc-scrollable/src/index.js` | absent-node/resize behavior | +| `CONT-R-005` failures | `packages/node_modules/@webex/react-container-file-downloader/src/index.test.js`, `test/journeys/specs/space/index.js` | integrated error display | + +## Traceability + +- Architecture/contracts: `../ARCHITECTURE.md`, `../CONTRACTS.md`. +- Listener rule: `../rules/clean-up-runtime-listeners.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/meetings-spec.md b/ai-docs/modules/meetings-spec.md new file mode 100644 index 000000000..1ae28cde4 --- /dev/null +++ b/ai-docs/modules/meetings-spec.md @@ -0,0 +1,306 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/meetings-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Meetings — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `meetings` | +| Source path(s) | `packages/node_modules/@webex/widget-meet/`, `widget-meetings/`, `redux-module-meetings/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 92% assessed 2026-07-22; entrypoints, destinations, legacy events, create/join/media/leave flow, state, UI, and current tests covered; lifecycle TODOs remain gaps | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Claims come from package entrypoints, containers/enhancers/handlers, meeting actions/reducer, UI tests, and Space journey meeting intent. The legacy namespace notice supplies compatibility history only. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Protected `@ciscospark/widget-meet` README and rename notice | compatibility | verified | Export Stability records the current namespace; legal material remains protected. | +| Space/Recents call and meeting guidance | host flow/tests | verified | Use Cases and Test Strategy connect incoming/start/join/hang-up behavior to current code. | +| Current implementation comments/TODOs | design gaps | used | Missing media-stopped and meeting stopped/destroyed handling is explicit in Pitfalls and tests. | + +## Overview + +This capability contains two related legacy widgets. `widget-meet` integrates the older media/Mercury call stack and emits call lifecycle host events. Its membership event names are exported constants but have no publisher in current source. `widget-meetings` uses the SDK meetings plugin plus `redux-module-meetings` to create, join, add media, and leave meetings while rendering inactive/active states. + +Both accept typed destination identifiers and compose through the shared widget base. The newer reducer intentionally stores meeting IDs and readiness state rather than SDK meeting objects; the SDK meeting collection remains the object source of truth. + +## Purpose / Responsibility + +Own embeddable meeting/call presentation and the client lifecycle from destination lookup through create/join/media/leave. Remote meeting membership, media transport, and service truth remain owned by the Webex SDK/services. + +## Stack + +JavaScript, React/PropTypes, Redux/Immutable.js, recompose, react-intl, Webex JS SDK meeting/media/Mercury plugins, shared media components, Jest, and Space browser journeys. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── widget-meet/src/ # legacy call widget, media/Mercury enhancers, host events +├── widget-meetings/src/ # create/join/leave UI, selectors, handlers, setup +└── redux-module-meetings/src/ # meeting thunks, ID indexes, media-ready state +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/widget-meet/src/index.js` | legacy widget export, destination types, enhancer composition | +| `packages/node_modules/@webex/widget-meet/src/events.js` | legacy call/membership event strings and payload builder | +| `packages/node_modules/@webex/widget-meetings/src/index.js` | meetings widget export, reducers, destination types | +| `packages/node_modules/@webex/widget-meetings/src/container.js` | public destination props and active/inactive UI wiring | +| `packages/node_modules/@webex/redux-module-meetings/src/actions.js` | create/join/add-media/leave thunks and SDK event binding | +| `packages/node_modules/@webex/redux-module-meetings/src/reducer.js` | destination/locus/ID indexes and meeting readiness state | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.widget.meet` | SDK/React/event | default Meet widget + reducers/destination types | legacy call experience and host call events | public semver | `packages/node_modules/@webex/widget-meet/src/index.js` | `../CONTRACTS.md` | +| `rw.widget.meetings` | SDK/React | default Meetings widget + reducers/destination types | meetings-plugin create/join/media/leave UI | public semver | `packages/node_modules/@webex/widget-meetings/src/index.js` | `../CONTRACTS.md` | +| `rw.state.meetings` | SDK/Redux | action thunks, reducer, `initialState`, `buildDestinationLookup` | shared meeting state and operations | public semver | `packages/node_modules/@webex/redux-module-meetings/src/index.js` | `../CONTRACTS.md` | +| `rw.meet.events` | event | emitted `calls:created/connected/disconnected`; defined-only `memberships:notified/connected/declined/disconnected` constants | legacy host lifecycle notifications and definition-only compatibility names | exact emitted strings stable; membership constants are not active events without a publisher | `packages/node_modules/@webex/widget-meet/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | `../CONTRACTS.md` | + +Compatibility notes: + +- Destination values remain `sip`, `email`, `userId`, `spaceId`, and `pstn`. +- `widget-meet` and `widget-meetings` are distinct public packages; do not silently replace one with the other. + +## Requires (dependencies) + +- Shared widget runtime/auth/intl and Webex SDK meetings/media/Mercury plugins. +- Legacy media Redux package for `widget-meet`; meetings Redux package for `widget-meetings`. +- Browser audio/video/media-stream capability and host permissions. +- Destination context from the host/Space/Recents flow. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `MEET-R-001` | Widgets accept only supported destination types/IDs and expose the same destination string set. | Hosts need one predictable target contract across Space/Meet/Meetings. | `packages/node_modules/@webex/widget-meet/src/index.js`, `packages/node_modules/@webex/widget-meetings/src/index.js`, `packages/node_modules/@webex/widget-meetings/src/container.js` | `test/journeys/specs/space/startup-settings.js`, `test/journeys/specs/space/index.js` | Invalid destination tests are sparse in Meetings. | PRESENT | +| `MEET-R-002` | `createAndJoinMeeting` creates through the SDK, stores ID lookup state, binds media events, joins, and resolves the meeting. | Ordered state creation prevents selectors from referencing an unindexed SDK object. | `packages/node_modules/@webex/redux-module-meetings/src/actions.js` | None found for the action path. | Major unit-test gap. | WEAK | +| `MEET-R-003` | Meeting Redux stores IDs and readiness flags by destination/locus/ID; SDK collection remains object source of truth. | Avoids placing mutable SDK meeting objects into immutable Redux state. | `packages/node_modules/@webex/redux-module-meetings/src/reducer.js` | `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.test.js` | Reducer lacks direct tests. | PRESENT | +| `MEET-R-004` | Adding media obtains streams with explicit send/receive settings and adds them to the located meeting. | Media direction must match host/user intent and SDK lifecycle. | `packages/node_modules/@webex/redux-module-meetings/src/actions.js` | ActiveMeeting/MeetingsWidget tests | Promise rejection path lacks dedicated tests. | PRESENT | +| `MEET-R-005` | Leave locates the meeting, invokes SDK `leave()`, then records `joined=false`; errors reject to the caller/error enhancer. | UI/state must not claim leave before the SDK operation succeeds. | `packages/node_modules/@webex/redux-module-meetings/src/actions.js`, `packages/node_modules/@webex/redux-module-meetings/src/reducer.js`, `packages/node_modules/@webex/widget-meetings/src/handlers/index.js` | `packages/node_modules/@webex/widget-meetings/src/components/ActiveMeeting.test.js`, `test/journeys/specs/space/index.js` | stopped/destroyed cleanup handling remains unimplemented. | PRESENT | +| `MEET-R-006` | Active/inactive/error/loading UI and legacy call events reflect current meeting/media state and remain keyboard/accessibility compatible. | The visible lifecycle and host callbacks are the consumer contract. | `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.js`, `packages/node_modules/@webex/widget-meet/src/container.js`, `packages/node_modules/@webex/widget-meet/src/events.js` | `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.test.js`, `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/specs/space/guest.js` | direct axe coverage is through Space composition. | PRESENT | + +## Design Overview + +The newer flow separates SDK object ownership from Redux indexing. A thunk asks `sdkInstance.meetings` to create or locate a meeting, the reducer maps destination/locus identifiers to the SDK meeting ID, and selectors retrieve the object/readiness needed by the connected UI. Media-ready events update boolean state; handlers map start/leave controls to thunks. + +The legacy Meet widget instead composes media and Mercury enhancers around its connected view and formats call events for hosts. Membership strings remain exported but are not emitted by `withEventHandler`. Keeping both packages preserves consumers while allowing the newer meetings-plugin model to coexist. + +## Data Flow + +```mermaid +flowchart LR + Host[Destination + credentials] --> Widget[Meet or Meetings widget] + Widget --> Runtime[Shared widget runtime] + Runtime --> SDK[Webex SDK meetings/media/Mercury] + SDK --> Thunks[Meeting/media thunks and event handlers] + Thunks --> Redux[ID indexes + readiness/call state] + Redux --> Selector[Meeting selector] + Selector --> UI[Inactive/Active meeting UI] + UI --> HostEvents[Legacy call events/callbacks] +``` + +Transport is in-process Redux/React plus promise/event-based Webex SDK calls and browser media streams. + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| create/join | Create and join | create/join rejection | +| add media | Media acquisition | missing meeting/media failure | +| leave | Leave lifecycle | lookup/leave rejection | + +```mermaid +sequenceDiagram + participant U as User/Host + participant W as Meetings widget + participant A as Redux thunk + participant S as SDK meetings + participant R as Redux state + U->>W: start meeting + W->>A: createAndJoinMeeting(destination) + A->>S: create(destinationId) + alt create succeeds + A->>R: store meeting ID/indexes + A->>S: bind media events + join() + S-->>R: joined=true + else create/join rejects + S-->>W: rejected promise/error UI + end +``` + +```mermaid +sequenceDiagram + participant W as Active UI + participant A as addMedia thunk + participant S as SDK meeting collection + W->>A: media settings + meetingId + A->>S: locate meeting + alt meeting found + A->>S: getMediaStreams(settings) + S-->>A: local stream/share + A->>S: addMedia(streams, settings) + else missing/rejected + A-->>W: error/rejected operation + end +``` + +```mermaid +sequenceDiagram + participant U as User + participant W as Meeting UI + participant A as leave thunk + participant S as SDK meeting + participant R as Redux state + U->>W: leave + W->>A: destination + A->>S: locate + leave() + alt success + A->>R: joined=false + R-->>W: inactive UI + else lookup/leave failure + A-->>W: error; joined state not falsely cleared + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class ConnectedMeetingsWidget + class MeetingsWidget + class ActiveMeeting + class InactiveMeeting + class MeetingActions + class MeetingsReducer + class SDKMeetingCollection + ConnectedMeetingsWidget *-- MeetingsWidget + MeetingsWidget *-- ActiveMeeting + MeetingsWidget *-- InactiveMeeting + ConnectedMeetingsWidget --> MeetingActions + MeetingActions --> SDKMeetingCollection + MeetingActions --> MeetingsReducer +``` + +## Use Cases + +- **UC-1 Start a destination meeting:** host supplies destination → user starts → SDK creates/joins → UI becomes active and media can attach. +- **UC-2 Join/add media:** active meeting requests configured send/receive streams → SDK returns media → UI renders local/remote media readiness. +- **UC-3 Leave/decline/hang up:** user action invokes SDK lifecycle → state/event/UI transitions to inactive or declined. +- **UC-4 Answer incoming call from Recents:** host receives a call object/room context → mounts Space/Meet flow with that call → lifecycle events report progress. +- **UI flow:** loading/error → inactive call control → active meeting media/leave controls → inactive after leave. +- **Cross-service flow:** Webex meeting/media/Mercury plugins own remote meeting and streams; Redux holds indexes/readiness only. + +## State Model + +- New meetings state: `byDestination`, `byLocusUrl`, and `byId`; each ID maps to `joined`, `hasLocalMedia`, `hasRemoteVideo`, and `hasRemoteAudio`. +- Legacy Meet state combines media/call and widget-specific error/destination details. +- Transitions come from create/store, join, media-ready, leave, incoming call/membership events, and SDK errors. + +## Business Rules & Invariants + +- `buildDestinationLookup` requires both destination ID and type and creates the stable `${type}-${id}` key. +- Never store the SDK meeting object in immutable Redux state; locate it in `meetingCollection.meetings` by ID. +- Record `joined=true/false` only after corresponding SDK promise success. +- Ignore falsey `media:ready` events; set only the readiness flag matching `local`, `remoteVideo`, or `remoteAudio`. + +## Concurrency & Reactive Flow + +- Create/join, media acquisition/addition, and leave are promise chains; consumers must handle rejection. +- Media-ready events can arrive independently and merge into existing state. +- Legacy Mercury/media enhancers listen for call changes; listener teardown and duplicate prevention are mandatory. Exported membership constants do not establish a listener or publisher. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Inactive + Inactive --> Creating: start + Creating --> Joining: meeting created + indexed + Creating --> Error: create rejected + Joining --> Active: join resolved + Joining --> Error: join rejected + Active --> AddingMedia: request streams + AddingMedia --> Active: media added/readiness events + AddingMedia --> Error: media failure + Active --> Leaving: leave + Leaving --> Inactive: leave resolved + Leaving --> Error: leave rejected +``` + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| missing destination/meeting lookup input | thrown lookup error | supply valid destination type/id or meeting ID | +| SDK collection lacks meeting | thrown lookup error | recreate/refresh meeting state; do not fabricate object | +| create/join/media/leave rejection | rejected promise / widget error enhancer | show error and allow retry/leave/remount as appropriate | +| browser media permission/capability absent | media promise failure/no ready event | request permission or run audio-only/compatible browser path | +| meeting destroyed/stopped externally | current implementation gap leaves possible stale state | do not claim full recovery; add handlers/tests before relying on it | + +## Pitfalls + +- `bindMeetingEvents` explicitly lacks media-stopped and meeting stopped/destroyed handlers; stale readiness/index state is a known gap. +- The call object belongs to the SDK and is mutable/non-serializable; keep it outside immutable meeting state and avoid logging/JSON conversion. +- Legacy `widget-meet` membership constants omit the `calls:` prefix used by Space's defined-only call-membership constants. Preserve the exported names, but do not describe either set as emitted until a publisher exists. +- Destination compatibility is shared but implementation paths differ between legacy Meet and newer Meetings. + +## Module Do's / Don'ts + +- DO locate meeting objects through the SDK collection and keep Redux to IDs/readiness. +- DO update UI/status only after SDK lifecycle success and handle promise rejection. +- DON'T merge the two public widget packages or normalize their event strings without a migration plan. + +## Export Stability + +`@ciscospark/widget-meet` was renamed to `@webex/widget-meet`; install/import the current package. Both current widget entrypoints, destination constants, named reducers/actions, and legacy event strings are semver-sensitive. + +## Host Integration & Theming + +Legacy widgets mount through the shared React/browser/data runtime and require host credentials/SDK plus media permissions. Space can embed Meet as an activity; Recents can pass incoming call context. Styles and video/audio elements depend on browser and Momentum UI behavior. + +## Key Design Trade-off + +- Storing only meeting IDs/readiness in Redux favors serializable, immutable state and SDK ownership over easy standalone state inspection; selectors/actions must coordinate with the live SDK collection. + +## Test-Case Strategy (module) + +Existing Jest tests cover active/inactive/aggregate Meetings UI. Space journeys cover pre-call, hang-up before answer, decline, in-call hang-up, event data, guest calling, startup `startCall`, and data API calling. Add direct action/reducer tests for create/join/media/leave, rejection, lookup, media-stopped, destroyed meetings, and listener cleanup. + +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| `MEET-R-001` destination/UI | `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.test.js`, `test/journeys/specs/space/startup-settings.js` | invalid destination unit cases | +| `MEET-R-002` create/join | None found for meeting thunk | blocking test gap before behavioral change | +| `MEET-R-003` ID/readiness model | `packages/node_modules/@webex/redux-module-meetings/src/reducer.js`, `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.test.js` | direct reducer tests | +| `MEET-R-004` add media | `packages/node_modules/@webex/widget-meetings/src/components/ActiveMeeting.test.js` | SDK promise/event unit tests | +| `MEET-R-005` leave | `test/journeys/specs/space/index.js`, `test/journeys/specs/space/guest.js`, `test/journeys/specs/space/data-api.js` | rejected leave and destroyed meeting | +| `MEET-R-006` call UI/events | `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/specs/space/index.js` | direct accessibility/payload tests | + +## Traceability + +- Architecture: `../ARCHITECTURE.md`; registry: `../SPEC_INDEX.md`; contracts/state: `../CONTRACTS.md`, `../SERVICE_STATE.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/recents-spec.md b/ai-docs/modules/recents-spec.md new file mode 100644 index 000000000..939061c5d --- /dev/null +++ b/ai-docs/modules/recents-spec.md @@ -0,0 +1,305 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/recents-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Recents — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `recents` | +| Source path(s) | `packages/node_modules/@webex/widget-recents/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 95% assessed 2026-07-22; entrypoint, options, current events, setup/listener flows, state, failure modes, and journey intent covered | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Current `src/index.js`, public PropTypes/defaults, `src/events.js`, setup/listeners, reducers/selectors, Jest tests, and journey specs are authoritative. Protected guides contribute intent but cannot add an event absent from current constants/listeners. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Existing Recents overview/install/configuration | overview/API/UI | verified/corrected | Supported options, modes, host APIs, teardown, and browser support are placed below. | +| Existing event guide and examples | events | verified/reference-only/stale | Current event set and semantics are in Public Surface/Requirements; payload examples remain native detail; notification examples are stale for this package. | +| Legacy namespace notice | compatibility | verified | Export Stability records the replacement package. | +| Journey test plan | tests | verified | Smoke, data API, global object, events, filters, calls, and accessibility are mapped in Test-Case Strategy. | + +## Overview + +Recents renders the authenticated user's Webex space list and reacts to realtime messages, read state, memberships, and incoming calls. It loads features/teams/spaces/avatars in state-driven stages, supports optional filtering/header controls, and emits selected/add/profile/sign-out/call events to the host. + +The module is a legacy enhanced widget: the package entrypoint composes the shared runtime and intl around a connected container. Setup and listeners translate SDK/Mercury work into shared Redux resource state; UI components render the header, empty state, and spaces list. + +## Purpose / Responsibility + +Own the embeddable recent-spaces list, its initial/realtime loading state, user selection/filter/header interactions, and Recents-specific host events. It does not own remote Webex space data. + +## Stack + +JavaScript, React 16, Redux/React-Redux, Immutable.js, recompose/decorators, react-intl, Webex JS SDK/Mercury, shared components/state modules, Jest, WebdriverIO, and axe-core. + +## Folder / Package Structure + +```text +widget-recents/src/ +├── index.js # public enhanced widget +├── container.js # props, rendering, user/host interactions +├── enhancers/setup.js # initial feature/team/space/avatar loading +├── enhancers/listeners.js # realtime activity/membership handling +├── events.js # current event names and payload builders +├── reducer.js / selector.js # widget-local state and view projection +└── components/ # header, profile, and empty-state UI +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/widget-recents/src/index.js` | public export and shared enhancer composition | +| `packages/node_modules/@webex/widget-recents/src/container.js` | public configuration defaults, rendering, selection/call/profile handlers | +| `packages/node_modules/@webex/widget-recents/src/events.js` | current event strings and payload construction | +| `packages/node_modules/@webex/widget-recents/src/enhancers/setup.js` | authenticated initial-loading workflow and default count | +| `packages/node_modules/@webex/widget-recents/src/enhancers/listeners.js` | realtime message/read/membership processing | +| `packages/node_modules/@webex/widget-recents/src/helpers.js` | space/user/avatar view helpers | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.widget.recents` | SDK/React | default enhanced Recents widget; named reducers | render/load recent spaces | public semver | `packages/node_modules/@webex/widget-recents/src/index.js` | `../CONTRACTS.md` | +| `rw.recents.options` | prop/data API | `basicMode`, header/filter/profile toggles, `spaceLoadCount`; accepted-only `muteNotifications` | host configuration | defaults and data names are public; `muteNotifications` is declared but has no read/use in current Recents source | `packages/node_modules/@webex/widget-recents/src/container.js` | `../CONTRACTS.md` | +| `rw.recents.events` | event | messages, room read/unread/selected, call, membership, add/profile/sign-out | host integration | exact strings stable | `packages/node_modules/@webex/widget-recents/src/events.js` | `../CONTRACTS.md` | + +Compatibility notes: + +- Browser global name is `recentsWidget`; data API toggle is `webex-recents`; teardown uses shared `remove()`. +- Current options default to `basicMode=false`, Add off, filter/profile on, profile menu off, and `spaceLoadCount=25`. +- `muteNotifications` remains an accepted PropTypes compatibility name but is currently unused and has no documented runtime effect. + +## Requires (dependencies) + +- Shared widget runtime/auth/Redux setup and current-user state. +- Spaces, activities, users, teams, media/calls, errors, features, Mercury, avatar, and shared list/loading/error components. +- Webex SDK service and realtime plugins; host credentials/SDK instance; browser DOM/event support. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `RECENTS-R-001` | After SDK auth/registration, Recents obtains feature values, connects Mercury, loads an initial bounded space list, then enriches avatars/teams without duplicate requests. | Staged loading provides responsive encrypted lists and prevents lifecycle re-entry from duplicating work. | `packages/node_modules/@webex/widget-recents/src/enhancers/setup.js` | `packages/node_modules/@webex/widget-recents/src/enhancers/setup.test.js` | Network failure recovery coverage is incomplete. | PRESENT | +| `RECENTS-R-002` | `basicMode=false` uses encrypted conversation loading; `true` uses Webex REST/Hydra and marks the one-stage list complete. | Encryption behavior is security-significant and explicitly controlled by the host. | `packages/node_modules/@webex/widget-recents/src/enhancers/setup.js` | `test/journeys/specs/recents/global/startup-settings.js` | Basic mode removes end-to-end encryption by design. | PRESENT | +| `RECENTS-R-003` | Realtime message/read/membership changes normalize state and emit current host events, excluding own-message unread behavior where implemented. | Host and UI must observe consistent state/event transitions. | `packages/node_modules/@webex/widget-recents/src/enhancers/listeners.js`, `packages/node_modules/@webex/widget-recents/src/events.js` | `test/journeys/specs/recents/global/basic.js` | Full payload snapshots are absent. | PRESENT | +| `RECENTS-R-004` | Selecting a space or call control emits `rooms:selected`, with `action: call` only for call selection; incoming calls emit `calls:created`. | Hosts use these distinctions to open Space/call experiences. | `packages/node_modules/@webex/widget-recents/src/container.js` | `test/journeys/specs/recents/global/basic.js` | Call object serialization is intentionally avoided in logs. | PRESENT | +| `RECENTS-R-005` | Header/filter/profile controls follow their option defaults and emit Add/Profile/Sign-out events only through current handlers. | Optional host UI must be predictable and backward-compatible. | `packages/node_modules/@webex/widget-recents/src/container.js` | `packages/node_modules/@webex/widget-recents/src/components/RecentsHeader.test.js`, `test/journeys/specs/recents/global/space-list-filter.js` | Sign-out side effect is host-owned; widget emits intent. | PRESENT | +| `RECENTS-R-006` | Ready/loading/empty/error rendering derives from fetch/filter/status state and remains accessible. | Users need deterministic feedback and the test plan requires axe-clean behavior. | `packages/node_modules/@webex/widget-recents/src/container.js` | Recents smoke/data/global journeys | Some error variants lack journey coverage. | PRESENT | + +## Design Overview + +Setup is a guarded state machine keyed by SDK and widget-status flags. It fetches feature settings, connects the realtime channel, loads encrypted or basic-mode spaces, enriches direct-space users/avatars, and fetches teams. Realtime listeners then update cached space/activity/membership state and invoke the container's event callback. + +The connected container deliberately rerenders only for relevant list/error/widget/call references. It keeps host interaction logic—selection, call selection, Add/Profile/Sign-out, filter, and scrolling—close to the rendered list, while SDK work remains in thunks/enhancers. + +## Data Flow + +```mermaid +flowchart LR + Host[Options + credentials] --> Runtime[Shared widget runtime] + Runtime --> Setup[Recents setup] + Setup --> SDK[Webex SDK REST/encrypted APIs + Mercury] + SDK --> Redux[spaces/users/teams/features/media/widget state] + Redux --> Selector[Recents selector] + Selector --> View[Header + SpacesList + empty/error UI] + SDK -. realtime .-> Listeners[Recents listeners] + Listeners --> Redux + View --> Events[Host event callback/DOM dispatch] +``` + +Remote transport is Webex SDK promises and Mercury events; internal transport is Redux actions/selectors and React props; outward transport is callback/CustomEvent/ampersand events. + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| initial load | Authenticated list setup | waits, encrypted/basic alternatives, failure status | +| realtime update | Message/read/membership processing | missing cached space/fetch alternative | +| user selection/header action | Host intent event | option/unknown target branches | + +```mermaid +sequenceDiagram + participant R as Recents setup + participant S as Webex SDK + participant X as Redux + alt SDK not ready + R-->>R: wait without fetching + else ready + R->>S: fetch feature flags + teams + R->>S: connect Mercury + alt basicMode + R->>S: fetch REST spaces(max) + else encrypted mode + R->>S: fetch encrypted conversations(max) + S-->>X: incremental decrypted spaces + R->>S: fetch avatars + end + S-->>X: mark initial/all spaces loaded + end +``` + +```mermaid +sequenceDiagram + participant M as Mercury + participant L as Recents listener + participant X as Redux + participant H as Host + M->>L: activity or membership event + alt cached space exists + L->>X: update space/activity/read/membership state + else target must be fetched + L->>X: fetch/store related space + end + L-->>H: current event name + constructed detail +``` + +```mermaid +sequenceDiagram + participant U as User + participant C as Recents container + participant H as Host + U->>C: select space/call/add/profile/sign-out + alt control enabled and target known + C-->>H: rooms:selected or option-specific event + else optional control hidden/disabled + C-->>U: no action surface + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class RecentsWidget + class SetupEnhancer + class ListenerEnhancer + class RecentsSelector + class RecentsReducer + class RecentsHeader + class SpacesList + RecentsWidget --> SetupEnhancer + RecentsWidget --> ListenerEnhancer + RecentsReducer --> RecentsSelector + RecentsSelector --> RecentsWidget + RecentsWidget *-- RecentsHeader + RecentsWidget *-- SpacesList +``` + +## Use Cases + +- **UC-1 Browse recents:** authenticated user opens widget → initial spaces load → avatars/teams/features enrich → list/empty state appears. Evidence: setup code and Recents journeys. +- **UC-2 React to incoming activity:** Webex emits message/read/membership change → listeners normalize space/activity → list/unread state changes → host receives supported event. +- **UC-3 Select/open/call a space:** user selects row or call control → `rooms:selected` payload identifies target/action → host opens its chosen experience. +- **UC-4 Filter/manage header:** user filters list or clicks configured Add/Profile/Sign-out control → local view/event updates without inventing service-side behavior. +- **UI flow:** loading → list or empty/error; optional header contains filter/Add/profile/menu; list supports unread and call indicators. +- **Cross-service flow:** Webex REST/encrypted conversation APIs provide the list; Mercury drives updates; people/team/feature/avatar SDK operations enrich display data. + +## State Model + +- Widget-local state tracks initial/all-space, avatar/team/feature fetch flags, scroll position, and keyword filter. +- Shared state tracks spaces by ID/list, users, teams, activities, media/incoming calls, errors, SDK/Mercury status, and features. +- Status flags are guards: a fetch/connect starts only when its `is*`/`has*` state permits. + +## Business Rules & Invariants + +- `spaceLoadCount <= 0` falls back to 25. Enforced in setup. +- Direct-space titles/avatars need the participant other than the current user; store/fetch that user before final display. +- Basic mode is an explicit opt-in and cannot be presented as encrypted. +- `rooms:selected` call payload includes `action: call`; ordinary selection does not. + +## Concurrency & Reactive Flow + +- Initial load, decryption, avatar fetches, team/feature fetches, and Mercury events run asynchronously; status flags prevent duplicate starts. +- Encrypted spaces may resolve incrementally; `Promise.all` tracks avatar enrichment without blocking initial state semantics beyond implemented stages. +- Realtime activity can precede cached space availability; listeners fetch/construct the missing context before normal processing where supported. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> WaitingForSDK + WaitingForSDK --> ConnectingRealtime: authenticated + registered + ConnectingRealtime --> LoadingSpaces: Mercury connected + LoadingSpaces --> LoadingAvatars: encrypted mode + spaces ready + LoadingSpaces --> Ready: basic mode complete + LoadingAvatars --> Ready + Ready --> Ready: realtime update/filter/scroll + LoadingSpaces --> Error: SDK rejection + LoadingAvatars --> Error: unrecovered failure + Error --> LoadingSpaces: retry/remount +``` + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| auth/registration missing | not-ready/loading | provide valid token/SDK and await registration | +| space/feature/team/avatar request fails | rejected thunk/status/error store | retry/remount; inspect SDK logs; keep already loaded state where implemented | +| no spaces or filter has no matches | intentional empty state | change filter or use Add control if enabled | +| incoming call lacks conversation URL | fallback call ID used as space identity | host handles limited context in `calls:created` | +| event payload contains call object | call omitted from logger but still delivered to callback | do not stringify/log the raw call object | + +## Pitfalls + +- Protected event docs list `notifications:created/clicked`, but current Recents constants do not; they are not current contracts. +- A source comment says pagination is to come; current encrypted/basic loading marks all spaces fetched after its one implemented stage. +- `handleProfileClick` emits `currentUserWithAvatar`, not every injected user-shaped prop. +- Event docs may show older payload fields/examples; current constructors are authoritative. +- `basicMode` changes the security properties of loading and must never be enabled as a neutral performance tweak. + +## Module Do's / Don'ts + +- DO guard async setup with widget-status flags and route host events through `handleEvent`/current constructors. +- DON'T fetch lists directly from the view, duplicate Mercury listeners, or document legacy notification events as current. + +## Export Stability + +`@ciscospark/widget-recents` was renamed to `@webex/widget-recents`; install/import the current namespace. Preserve the default package entrypoint, `recentsWidget`, `webex-recents`, option defaults, and current event strings through normal semver/deprecation rules. + +## Host Integration & Theming + +Hosts import the React package or mount through `window.webex.widget(element).recentsWidget(options)` / `data-toggle="webex-recents"`. CDN/imported styles are required. Hosts own follow-up behavior for selected/add/profile/sign-out events and must supply compatible credentials/SDK. + +## Key Design Trade-off + +- Encrypted mode favors security and incremental decryption/avatars over the simpler REST list; `basicMode` preserves a simpler integration at the explicit cost of end-to-end encryption. + +## Test-Case Strategy (module) + +Jest covers helpers, setup, header/profile/empty components. WebdriverIO covers data API and global instantiation, group/direct updates, unread/read, selection/call buttons, memberships, incoming calls, filter/startup settings, multiple widgets, demo auth forms, and axe accessibility. Add negative cases for rejected SDK operations and listener duplication. + +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| `RECENTS-R-001` staged setup | `packages/node_modules/@webex/widget-recents/src/enhancers/setup.test.js` | failure/retry permutations | +| `RECENTS-R-002` basic/encrypted mode | `test/journeys/specs/recents/global/startup-settings.js` | explicit encryption assertion | +| `RECENTS-R-003` realtime/events | `test/journeys/specs/recents/global/basic.js` | full payload snapshots and stale-event negative test | +| `RECENTS-R-004` selection/call | `test/journeys/specs/recents/global/basic.js`, `test/journeys/specs/recents/dataApi/basic.js` | unknown/missing target negative case | +| `RECENTS-R-005` header/filter | `packages/node_modules/@webex/widget-recents/src/components/RecentsHeader.test.js`, `test/journeys/specs/recents/global/space-list-filter.js` | sign-out host effect remains external | +| `RECENTS-R-006` ready/error/a11y | `test/journeys/specs/smoke/widget-recents/index.js`, `test/journeys/specs/recents/global/basic.js` | more error variants | + +## Traceability + +- Repo architecture: `../ARCHITECTURE.md`; registry: `../SPEC_INDEX.md`; contracts/state: `../CONTRACTS.md`, `../SERVICE_STATE.md`. +- Coverage state, source routing, profile, and contract baseline: `.sdd/manifest.json`. diff --git a/ai-docs/modules/shared-ui-components-spec.md b/ai-docs/modules/shared-ui-components-spec.md new file mode 100644 index 000000000..ab39d26cb --- /dev/null +++ b/ai-docs/modules/shared-ui-components-spec.md @@ -0,0 +1,234 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/shared-ui-components-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Shared UI Components — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `shared-ui-components` | +| Source path(s) | `packages/node_modules/@webex/react-component-*/`, `private-react-component-*/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 93% assessed 2026-07-22; all entrypoints and component families covered, with direct behavior detail concentrated on high-coupling components | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Package entrypoints, PropTypes/TypeScript types, styles, stories, and adjacent tests define behavior. A story demonstrates a supported rendering state but is not proof of error handling. Protected legacy rename notices establish namespace history only. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Legacy `@ciscospark/react-component-*` READMEs | namespace migration | verified | Current `@webex` entrypoints and compatibility policy are in Export Stability. | +| Component source and package metadata | exports, props, rendering | authoritative | Public Surface, Requirements, Design Overview. | +| Adjacent Jest tests and Storybook stories | behavior examples | verified | Test-Case Strategy. | + +## Overview + +This capability is the visual vocabulary used by widgets and containers: activity renderers, buttons, media elements, avatars, file affordances, adaptive-card inputs, separators, loading/error views, and utility functions. Most packages export one presentational React component; a few deliberately connect to Redux or browser APIs. + +## Purpose / Responsibility + +Provide reusable, composable UI primitives with stable package entrypoints and explicit props. Components must not silently take ownership of widget-level authentication, routing, or remote-resource lifecycle. + +## Stack + +JavaScript/TypeScript, React 16, PropTypes, CSS modules/SCSS, Momentum UI, react-intl, Immutable.js where supplied by callers, Jest, react-test-renderer, and Storybook. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── react-component-*/src/ # public components and utilities +└── private-react-component-*/src/ # repository-internal examples/helpers +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/react-component-activity-item/src/index.js` and the exact package paths indexed in `../CONTRACTS.md` | package public entrypoints and primary components | +| `packages/node_modules/@webex/react-component-utils/src/index.js` | public utility barrel | +| `packages/node_modules/@webex/react-component-adaptive-card/src/index.js` | adaptive-card DOM/Redux integration and cleanup | +| `packages/node_modules/@webex/react-component-activity-item/src/index.js` | post/share/system-message dispatch | +| `packages/node_modules/@webex/react-component-audio/src/index.js` and `packages/node_modules/@webex/react-component-video/src/index.js` | MediaStream-to-element binding | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.ui.components` | SDK/React | 54 `@webex/react-component-*` package entrypoints | independently reusable widget presentation | public semver; default/named exports and required props are stable | exact paths in the 54 `rw.ui.*` catalog rows; representative: `packages/node_modules/@webex/react-component-activity-item/src/index.js`, `packages/node_modules/@webex/react-component-video/src/index.js` | `../CONTRACTS.md` | +| `rw.ui.utils` | SDK | `@webex/react-component-utils` named barrel | shared files/components/date/activity/UUID/validation/adaptive-card helpers | public semver; removal/rename is breaking | `packages/node_modules/@webex/react-component-utils/src/index.js` | `../CONTRACTS.md` | + +Compatibility notes: + +The 54 non-private packages are public entrypoints. Most default-export a component named by the package; exact props and named exports remain source-defined at each `src/index.*`. + +High-coupling contracts include: + +- `ActivityItem` selects post, share, ECM-link, or system-message presentation from activity verb/content. +- `AdaptiveCard` renders SDK-backed card content, submits actions, replaces decrypted image URLs, and unmounts child React roots. +- `Audio` and `Video` assign a supplied `MediaStream` to `srcObject`; `Video` supports local-audio muting. +- `ButtonControls` chooses Momentum `CallControl` versus `ActivityButton` from each descriptor. + +## Requires (dependencies) + +- React/ReactDOM and host DOM/media support. +- Momentum UI, react-intl, CSS-module processing, and package-specific utilities. +- Selected components require Redux state/actions, the Webex SDK, or container packages; these dependencies must remain visible in their entrypoints. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `UI-R-001` | Each public component package preserves its default/named entrypoint and declared prop contract. | Widgets and external consumers import packages independently. | `packages/node_modules/@webex/react-component-activity-item/src/index.js`, `packages/node_modules/@webex/react-component-utils/src/index.js`, `ai-docs/CONTRACTS.md` | `packages/node_modules/@webex/react-component-activity-item/src/index.test.js` | Not every prop combination has a test. | PRESENT | +| `UI-R-002` | Activity rendering selects the component appropriate to verb/content and leaves unknown verbs empty. | Conversation feeds must not misrepresent activity types. | `packages/node_modules/@webex/react-component-activity-item/src/index.js` | `packages/node_modules/@webex/react-component-activity-item/src/index.test.js` | ECM/adaptive-card remote payloads depend on SDK shape. | PRESENT | +| `UI-R-003` | Stream components return no element without a stream and bind valid streams through element refs. | Avoid invalid playback nodes and keep media ownership with the caller. | `packages/node_modules/@webex/react-component-audio/src/index.js`, `packages/node_modules/@webex/react-component-video/src/index.js` | `packages/node_modules/@webex/react-component-audio/src/index.test.js` | Video has no adjacent Jest test; browser autoplay policy is external. | PRESENT | +| `UI-R-004` | Adaptive-card child roots are unmounted and transient submission status is dismissed. | Prevent leaked subtrees and stale status UI. | `packages/node_modules/@webex/react-component-adaptive-card/src/index.js` | `packages/node_modules/@webex/react-component-adaptive-card/src/index.test.js` | A two-second timer remains implementation-defined. | PRESENT | +| `UI-R-005` | Interactive controls expose labels/ARIA data and invoke supplied callbacks without owning business operations. | Accessibility and composability depend on caller-controlled actions. | `packages/node_modules/@webex/react-component-button-controls/src/index.js`, `packages/node_modules/@webex/react-component-incoming-call/src/index.js` | `packages/node_modules/@webex/react-component-button-controls/src/index.test.js`, `test/journeys/specs/smoke/widget-space/index.js` | Repository-wide journey coverage is uneven. | PRESENT | + +## Design Overview + +Components receive normalized data and callbacks from containers/widgets. Pure functions and small functional components dominate; class components are used where DOM refs or lifecycle cleanup are required. CSS modules isolate styles while stable `webex-*` class names support host inspection and existing tests. + +## Data Flow + +```mermaid +flowchart LR + Widget[Widget/container] --> Props[Props and callbacks] + Redux[Selected Redux state] --> Props + Props --> Component[Shared component] + Component --> DOM[Accessible DOM/media] + Component --> Callback[Caller callback/action] + SDK[SDK helper/data] --> Component +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| render, interact, update, dispose | Component lifecycle | absent data, callback ownership, and child-root cleanup share this actor/order group | + +```mermaid +sequenceDiagram + participant P as Parent + participant C as Component + participant D as DOM + P->>C: props/data/callbacks + C->>D: render accessible view + D->>C: user interaction + C-->>P: callback with component-owned value + opt component owns child DOM roots + P->>C: unmount + C->>D: detach/unmount children + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class WidgetOrContainer + class PresentationalComponent + class ComponentUtils + class MomentumUI + class ReduxConnectedComponent + WidgetOrContainer --> PresentationalComponent + PresentationalComponent --> ComponentUtils + PresentationalComponent --> MomentumUI + ReduxConnectedComponent --|> PresentationalComponent +``` + +## Use Cases + +- Render conversation activities using the activity verb, content, files, actors, and timestamps. +- Bind remote/local media streams to browser media elements. +- Compose labeled call or activity controls from descriptors. +- Render and submit adaptive cards while displaying sending/success/failure state. +- Reuse loading, error, avatar, badge, list, input, and separator primitives across widget packages. + +## State Model + +Most functional components are stateless projections. Stateful class components such as AdaptiveCard own only transient UI/DOM state: child roots, decrypted-image replacement readiness, and submission status. Parent widgets/containers continue to own domain and remote-resource state. + +## Business Rules & Invariants + +- Required props remain required at the component boundary; defaults cover only optional presentation. +- A component that receives a callback invokes it but does not duplicate the owning Redux/SDK operation. +- DOM-root, timer, listener, and object-URL resources created by a component require lifecycle cleanup. +- CSS and accessibility attributes are part of observable UI behavior even when they are not JavaScript exports. + +## Concurrency & Reactive Flow + +React prop/state updates may race SDK or image decryption results. Adaptive cards wait until all decrypted URLs are available before replacing rendered card content; asynchronous status changes must not update an unmounted subtree. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Empty + Empty --> Content: valid props/data + Content --> Active: interaction or async operation + Active --> Content: success/dismiss + Active --> ErrorView: component-supported failure state + Content --> Disposed: unmount + ErrorView --> Disposed: unmount +``` + +## Pitfalls + +- Some “component” packages connect Redux and are not pure. +- Stories are examples, not exhaustive specifications. +- Do not remove stable CSS classes merely because CSS modules generate local names. +- Adaptive-card failures use the SDK logger; callers must own broader recovery. +- Media autoplay, codec, and permission behavior remains browser-owned. + +## Module Do's / Don'ts + +- Do keep public entrypoints narrow and test behavior close to the component. +- Do clean resources created in lifecycle methods. +- Don't import widget orchestration into shared presentation. +- Don't change a named/default export or required prop without semver review. + +## Export Stability + +Public `@webex/react-component-*` entrypoints are semver contracts. The protected `@ciscospark/react-component-*` READMEs document that each legacy package moved to the identically suffixed `@webex` namespace; they are migration evidence, not additional runtime exports. Private-prefixed packages remain internal. + +## Host Integration & Theming + +Components inherit host font/style setup through the widget bundle, Momentum UI, CSS modules, and stable class hooks. Callers provide locale/intl, callbacks, SDK-backed values, and media streams. Avoid global styling beyond the existing font/theme entrypoints. + +## Key Design Trade-off + +The many small packages improve independent reuse and tree selection, but multiply public entrypoints and upgrade obligations. Preserve package boundaries unless a migration plan accounts for every consumer. + +## Test-Case Strategy (module) + +| Requirement | Current evidence | Focused gap | +|---|---|---| +| `UI-R-001` entrypoints/props | `packages/node_modules/@webex/react-component-activity-item/src/index.test.js`, `ai-docs/CONTRACTS.md` | automated export inventory | +| `UI-R-002` activity selection | `packages/node_modules/@webex/react-component-activity-item/src/index.test.js` | malformed/unknown activity matrix | +| `UI-R-003` stream binding | `packages/node_modules/@webex/react-component-audio/src/index.test.js`; no Video test found | browser autoplay rejection | +| `UI-R-004` cleanup/status | `packages/node_modules/@webex/react-component-adaptive-card/src/index.test.js` | fake-timer/unmount race | +| `UI-R-005` accessibility/callbacks | `packages/node_modules/@webex/react-component-button-controls/src/index.test.js`, `test/journeys/specs/smoke/widget-space/index.js` | keyboard matrix per composite | + +## Traceability + +- Architecture and package inventory: `../ARCHITECTURE.md`, `../CONTRACTS.md`. +- Coding conventions: `../patterns/react-component-entrypoint.md`, `../rules/preserve-public-entrypoints.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/space-messaging-spec.md b/ai-docs/modules/space-messaging-spec.md new file mode 100644 index 000000000..c904924d1 --- /dev/null +++ b/ai-docs/modules/space-messaging-spec.md @@ -0,0 +1,336 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/space-messaging-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Space and Messaging — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). This is the canonical capability spec for Space, Message, Files, and Roster packages. + +## Metadata + +| Field | Value | +|---|---| +| Module id | `space-messaging` | +| Source path(s) | `packages/node_modules/@webex/widget-space/`, `widget-message/`, `widget-files/`, `widget-roster/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 94% assessed 2026-07-22; all package entrypoints, current events, major flows, state, failures, and journey intent covered; sparse unit coverage remains for composed widget flows | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Requirements cite stable implementation and test paths only. Protected usage/event documents supplied reconciled intent; code constants, props, reducers, and tests decide conflicts. Missing tests are gaps, not inferred passes. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Existing widget usage/configuration guidance | overview, host API, UI | verified/corrected | Current options and destination behavior are in Public Surface, Requirements, UI Flow, and Host Integration. | +| Existing event guide and payload examples | events | verified/reference-only/stale | Current constant set is in Public Surface; detailed payload construction stays in code; unsupported notification/mention examples are flagged in Pitfalls. | +| Legacy package rename notices | compatibility | verified | Namespace mapping is in Export Stability; legal notices remain in their protected sources. | +| Journey test plan | tests | verified | Space smoke, primary, messaging, file, meet, guest, settings, data-API, and accessibility intent is mapped in Test-Case Strategy. | + +## Overview + +This capability presents a Webex space as an activity-oriented widget. `widget-space` selects a destination and composes message, meeting, file, and roster activities. The focused child widgets remain importable separately, while the full widget supplies navigation, destination setup, Redux state, SDK integration, error/loading UI, and host events. + +Maintainers should start at each package `src/index.js`, then follow Space `container.js`, `enhancers/setup.js`, reducer/selector, and the child widget entrypoint. Reusable rendering/state behavior belongs in Shared UI, Containers/HOCs, or Redux modules rather than being duplicated here. + +## Purpose / Responsibility + +Own the embeddable space experience: resolve a typed Webex destination, expose enabled activities, coordinate messaging/files/roster/meeting presentation, and report current host events. It does not own Webex remote data or SDK transport. + +## Stack + +JavaScript, React 16, PropTypes, Redux/React-Redux, Immutable.js, recompose, react-intl, Momentum UI, Webex JS SDK plugins, Jest, and WebdriverIO journeys; Babel/Rollup/Webpack build the package outputs. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── widget-space/src/ # full activity shell, setup, state, selectors, events +├── widget-message/src/ # conversation message composition/display +├── widget-files/src/ # files activity composition +└── widget-roster/src/ # people/roster activity composition +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/widget-space/src/index.js` | exported widget, reducers, destination types, event names, enhancer composition | +| `packages/node_modules/@webex/widget-space/src/container.js` | public props, defaults, activity UI, keyboard behavior, unregistration | +| `packages/node_modules/@webex/widget-space/src/constants.js` | activity and destination constants | +| `packages/node_modules/@webex/widget-space/src/events.js` | current host event strings | +| `packages/node_modules/@webex/widget-space/src/enhancers/setup.js` | authenticated setup, destination normalization, reload, Mercury, data fetch | +| child widget `src/index.js` files | child package exports and enhancer composition | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.widget.space` | SDK/React | default `SpaceWidget`; named `eventNames`, `reducers`, `destinationTypes` | full space experience | public semver; preserve props/events/destination strings | `packages/node_modules/@webex/widget-space/src/index.js` | `../CONTRACTS.md` | +| `rw.widget.message` | SDK/React | default Message widget; named reducers/destination types | focused messaging activity | public semver | `packages/node_modules/@webex/widget-message/src/index.js` | `../CONTRACTS.md` | +| `rw.widget.files` | SDK/React | default Files widget | focused file activity | public semver | `packages/node_modules/@webex/widget-files/src/index.js` | `../CONTRACTS.md` | +| `rw.widget.roster` | SDK/React | default Roster widget; named reducers | focused people/roster activity | public semver | `packages/node_modules/@webex/widget-roster/src/index.js` | `../CONTRACTS.md` | +| `rw.space.destinations` | SDK/prop | `email`, `userId`, `spaceId`, `sip`, `pstn` where accepted | identify target conversation/call | exact strings stable | `packages/node_modules/@webex/widget-space/src/constants.js` | `../CONTRACTS.md` | +| `rw.space.events` | event | emitted `messages:created`, `rooms:read`, `rooms:unread`, `calls:created/connected/disconnected`, and `activity:changed` | notify host of current Space/child behavior | exact emitted strings stable; payload additive only | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js` | `../CONTRACTS.md` | +| `rw.widget.space` | SDK/prop | `initialActivity` | choose the valid primary activity at startup | accepted prop/data attribute; defaults to `message` in the activity enhancer | `packages/node_modules/@webex/widget-space/src/enhancers/activity-menu.js` | `../CONTRACTS.md` | +| `rw.widget.space` | SDK/prop | `setCurrentActivity` | request a valid primary or secondary activity after mount | accepted prop; changes emit `activity:changed` | `packages/node_modules/@webex/widget-space/src/container.js`, `packages/node_modules/@webex/widget-space/src/enhancers/external-control.js` | `../CONTRACTS.md` | + +Compatibility notes: + +- Imported React, browser-global, and data-attribute forms remain supported through the shared runtime. +- Current `eventNames` is authoritative. Older event examples not present there are not active contracts. +- The exported `calls:memberships:*` constants have no publisher in current Space/Meet/Message source and are definition-only, not active emitted contracts. + +## Requires (dependencies) + +- `@webex/webex-widget-base` for Redux/SDK/auth/host integration and teardown. +- Message/files/roster/meet child widgets, shared components, containers, and Redux resource modules. +- Webex SDK authentication, device, conversation, rooms/people/meetings, Mercury, presence, search, team, feature, and flag plugins. +- Host credentials or a compatible authenticated SDK instance; browser DOM/media capabilities for UI/calling. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `SPACE-R-001` | The full widget accepts a supported destination type/id and resolves/stores the target only after SDK authentication and registration. | Prevents network/setup work against an unauthenticated SDK and keeps destination changes deterministic. | `packages/node_modules/@webex/widget-space/src/enhancers/setup.js` | `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js`, `test/journeys/specs/space/startup-settings.js` | PSTN/SIP behavior depends on SDK capability. | PRESENT | +| `SPACE-R-002` | Enabled `spaceActivities` determine message, meet, files, and people UI; disabling the initial activity produces an error rather than silently selecting an invalid activity. | Host configuration must predict visible navigation and startup state. | `packages/node_modules/@webex/widget-space/src/container.js`, `packages/node_modules/@webex/widget-space/src/enhancers/activity-menu.js` | `test/journeys/specs/space/startup-settings.js` | Some composition paths lack direct Jest tests. | PRESENT | +| `SPACE-R-003` | Message behavior supports sending/receiving, markdown, attachments, flags, deletion rules, mentions, and composer option controls through existing child/state packages. | These are the core promised space interactions and must remain compatible. | `packages/node_modules/@webex/widget-message/src/actions.js`, `packages/node_modules/@webex/widget-message/src/container.js`, `packages/node_modules/@webex/redux-module-activity/src/actions.js` | `test/journeys/specs/space/index.js`, `test/journeys/lib/test-helpers/space-widget/messaging.js` | Journey credentials/services required. | PRESENT | +| `SPACE-R-004` | The widget emits only event constants that have an observed publisher through host callbacks/browser event translation; defined-only `calls:memberships:*` constants are not claimed as emitted. | Constants alone do not prove a host event, and consumers need the implemented event boundary. | `packages/node_modules/@webex/widget-space/src/events.js`, `packages/node_modules/@webex/widget-space/src/enhancers/activity-menu.js`, `packages/node_modules/@webex/widget-space/src/enhancers/external-control.js`, `packages/node_modules/@webex/widget-meet/src/enhancers/withEventHandler.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | `test/journeys/lib/events.js`, `test/journeys/specs/space/index.js` | Payload examples in old docs may omit current fields; membership constants have no publisher. | PRESENT | +| `SPACE-R-005` | Keyboard navigation preserves tab roles, arrow/Home/End behavior, Meet-button focus handoff, and accessible labels. | The widget is customer-facing UI and journey plans require no accessibility violations. | `packages/node_modules/@webex/widget-space/src/container.js` | `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/specs/space/index.js` | Axe journeys are environment-dependent. | PRESENT | +| `SPACE-R-006` | Unmount/removal unregisters device/widget state through established teardown paths. | Prevents leaked registrations, listeners, stores, and duplicate host events. | `packages/node_modules/@webex/widget-space/src/container.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withRemoveWidget.js` | `packages/node_modules/@webex/widget-space/src/reducer.test.js` | End-to-end repeated mount/remove coverage is limited. | WEAK | + +## Design Overview + +The package exports a connected container wrapped by the shared widget enhancer and intl. `setup` is the state-driven coordinator: after SDK readiness it normalizes destinations, connects Mercury, fetches space/user/avatar data, and requests reload when destination changes. The container derives visible activity widgets, renders loading/error/content states, and forwards user/external-control activity changes. + +Child widgets encapsulate focused activity composition. Redux modules normalize remote resources and async status so views remain mostly declarative. This division avoids placing SDK state machines inside presentational components and lets the same Message/Files/Roster packages be reused outside the full Space shell. + +## Data Flow + +```mermaid +flowchart LR + Host[Host props/data attributes] --> Base[webex-widget-base] + Base --> Store[Widget Redux store] + Store --> Setup[Space setup enhancer] + Setup --> SDK[Webex SDK + Mercury] + SDK --> Modules[conversation/activity/space/user/media reducers] + Modules --> Selector[Space selector] + Selector --> Shell[Space container] + Shell --> Children[Message / Files / Roster / Meet] + Children --> SDK + Shell --> HostEvents[Host callback and DOM events] +``` + +Transport is in-process React/Redux for composition, promise-based Webex SDK calls for remote operations, and SDK/Mercury events for realtime updates. + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| initialize/change destination | Destination setup | waits for auth/registration; reload/error branches | +| send/receive content | Messaging/file flow | SDK rejection and error-state branch | +| switch/start activity | Activity flow | disabled/unavailable activity branch | + +```mermaid +sequenceDiagram + participant H as Host + participant S as Space setup + participant R as Redux modules + participant W as Webex SDK + H->>S: destinationType + destinationId + alt SDK not authenticated/registered + S-->>H: loading; no fetch + else ready + S->>R: store normalized destination + S->>W: connect Mercury and fetch target data + W-->>R: conversation/space/user/activity data + R-->>H: selector renders activities + end + opt destination changes + S->>R: reset conversation/errors and reload + end +``` + +```mermaid +sequenceDiagram + participant U as User + participant M as Message/File UI + participant R as Redux thunk + participant W as Webex SDK + participant H as Host + U->>M: send message or attachment + M->>R: dispatch operation + R->>W: SDK conversation/file call + alt success/realtime echo + W-->>R: normalized activity + R-->>M: rendered activity + M-->>H: messages:created when applicable + else rejection/decrypt/download failure + W-->>R: reject/error action + R-->>M: error display/retry affordance + end +``` + +```mermaid +sequenceDiagram + participant U as User/Host + participant A as Activity menu/control + participant C as Space container + participant H as Host callback + U->>A: select/set activity + alt activity enabled and available + A->>C: update primary/secondary activity + C-->>U: render selected child widget + C-->>H: activity:changed + else disabled or meeting unavailable + C-->>U: preserve current activity or render error/disabled state + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class SpaceWidget + class SetupEnhancer + class SpaceReducer + class SpaceSelector + class MessageWidget + class FilesWidget + class RosterWidget + class MeetWidget + SpaceWidget --> SetupEnhancer + SetupEnhancer --> SpaceReducer + SpaceReducer --> SpaceSelector + SpaceSelector --> SpaceWidget + SpaceWidget *-- MessageWidget + SpaceWidget *-- FilesWidget + SpaceWidget *-- RosterWidget + SpaceWidget *-- MeetWidget +``` + +The full widget owns orchestration and navigation; child widgets own activity presentation/operations; shared Redux modules own client representations and async transitions. + +## Use Cases + +- **UC-1 Open a space:** host supplies `spaceId`, email, or user ID → setup authenticates/resolves destination → enabled activities render. Evidence: `packages/node_modules/@webex/widget-space/src/enhancers/setup.js`, `test/journeys/specs/space/startup-settings.js`. +- **UC-2 Message and share files:** user composes text/markdown/attachment → Redux/SDK sends → realtime state renders the result and host events. Evidence: `test/journeys/lib/test-helpers/space-widget/messaging.js`. +- **UC-3 Inspect/manage participants:** user opens People → roster lists/counts/searches/adds participants → close returns to the primary activity. Evidence: `test/journeys/lib/test-helpers/space-widget/roster.js`. +- **UC-4 Start/answer a call:** user selects Meet or host passes call/start settings → call UI invokes SDK behavior → lifecycle events/status update. Evidence: `test/journeys/lib/test-helpers/space-widget/meet.js`. +- **UI flow:** loading/error → activity shell → message/files/people tabs plus Meet control → secondary activity overlays according to `secondaryActivitiesFullWidth`; keyboard navigation follows the tab/Meet rules. +- **Cross-service flow:** all remote data and call operations pass through supplied Webex SDK plugins; Mercury supplies realtime conversation/activity updates. + +## State Model + +- Widget state tracks destination, primary/secondary activity, reload/fetch status, and configuration-derived activity types. +- Combined reducers add conversation, activities, spaces, users, errors, media/calls, Mercury, presence, flags/features, and child-widget state. +- Triggers include host prop changes, setup lifecycle, SDK promise resolution/rejection, realtime events, user navigation, and teardown. + +## Business Rules & Invariants + +- An initial activity must be enabled; otherwise surface an error. Enforced in Space activity/setup logic. +- Destination strings and IDs must be normalized before fetch; email is lowercased and Hydra space IDs are decoded/cluster-aware. Enforced in `packages/node_modules/@webex/widget-space/src/enhancers/setup.js`. +- Meet is disabled for a space destination when the SDK has no preferred Webex site. Enforced in `packages/node_modules/@webex/widget-space/src/container.js`. +- Users may delete their own messages but not another person's; journey intent preserves this authorization-facing UI rule. + +## Concurrency & Reactive Flow + +- React lifecycle/setup runs repeatedly as SDK and destination props change; status flags prevent duplicate fetch/connect operations. +- Mercury activities and SDK call/media events asynchronously update Redux and may emit host events; listeners must not be registered twice. +- Destination changes reset existing conversation/errors before loading the replacement; do not let stale async results become the visible target. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> WaitingForSDK + WaitingForSDK --> ResolvingDestination: authenticated + registered + ResolvingDestination --> LoadingSpace: destination stored + LoadingSpace --> Ready: target data available + LoadingSpace --> Error: SDK/setup failure + Ready --> Reloading: destination/config requests reload + Reloading --> ResolvingDestination + Error --> Reloading: recoverable retry/change + Ready --> Removed: host remove/unmount + Error --> Removed +``` + +## UI Flow + +- Loading state precedes available target data; persistent/temporary errors render through `ErrorDisplay`. +- Message, Files, and People appear as tabs when enabled; Meet is a separate call control and can show active-call time. +- Empty/disabled initial activity, unavailable meeting site, incoming call, secondary activity, and teardown are non-happy paths that must remain visible/tested. + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| missing/invalid destination | setup cannot resolve/fetch; error state/UI | provide a supported type/id and rerender | +| SDK unauthenticated/unregistered | widget remains loading/not ready | supply valid credentials/SDK and allow registration | +| conversation/resource operation fails | Redux error/conversation error displayed | retry/change destination; inspect SDK logger | +| disabled initial activity | explicit widget error | enable the activity or select an enabled initial value | +| meeting unavailable for space | disabled Meet/error title | configure preferred Webex site or use another activity | +| file decrypt/download fails | promise/error state from file modules | retry when SDK/network is available | + +## Pitfalls + +- Existing protected event guides list `notifications:*` and `mention:clicked`, but the current Space event constants do not; do not expose them without an intentional code/spec change. +- `secondaryActivitiesFullWidth` defaults to `true` in current code even though older usage text described `false`; code is authoritative. +- `spaceActivities` is the current prop; older example code may use `activities`. +- The activity-menu/Meet focus order is hand-coded; DOM/class/role changes can regress keyboard navigation without obvious render failures. +- Webex remote objects may arrive encrypted or incrementally; do not assume list/detail/avatar data is synchronously complete. + +## Module Do's / Don'ts + +- DO normalize destinations through the existing constants/setup path and compose child widgets through package entrypoints. +- DO keep event strings/payload construction centralized in current event/helper files. +- DON'T duplicate SDK fetch/listener logic in presentational components or treat protected examples as newer than code. + +## Export Stability + +Public default/named exports, props, destination strings, browser/data API names, and events are semver-sensitive. Legacy namespace mapping remains: + +| Legacy package | Current package | +|---|---| +| `@ciscospark/widget-space` | `@webex/widget-space` | +| `@ciscospark/widget-message` | `@webex/widget-message` | +| `@ciscospark/widget-files` | `@webex/widget-files` | +| `@ciscospark/widget-roster` | `@webex/widget-roster` | + +## Host Integration & Theming + +Hosts may import React packages or mount the full widget through `window.webex.widget(element).spaceWidget(options)` / `data-toggle="webex-space"`. They provide auth/SDK and destination options, include the package Sass/CSS or CDN stylesheet, and consume callbacks/DOM events. Do not assume host React/theme versions beyond declared root/package dependencies. + +## Key Design Trade-off + +- The full widget favors reusable activity packages and centralized Redux/SDK orchestration over a single monolithic component. This preserves composability and host options but creates cross-package coordination and a larger compatibility surface. + +## Test-Case Strategy (module) + +Jest covers Space actions, reducer, selector, and setup; child/Redux/component tests cover focused behaviors. WebdriverIO supplies the end-to-end contract: activity navigation/roster, message send/receive/events, flags/deletion, files/markdown, meeting lifecycle, guest auth, startup options, data API, multiple widgets, and axe accessibility. Every change should add a positive assertion and a negative/recovery assertion at the lowest reliable tier. + +| Behavior / Requirement | Existing test evidence | Gap | +|---|---|---| +| `SPACE-R-001` destination/setup | `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js`, `test/journeys/specs/space/startup-settings.js` | broader invalid SIP/PSTN cases | +| `SPACE-R-002` activity configuration | `test/journeys/specs/space/startup-settings.js` | focused Jest coverage for every option combination | +| `SPACE-R-003` messaging/files/roster | `test/journeys/specs/space/index.js`, `test/journeys/lib/test-helpers/space-widget/messaging.js`, `test/journeys/lib/test-helpers/space-widget/roster.js` | remote-service dependent | +| `SPACE-R-004` events | `test/journeys/lib/events.js`, `test/journeys/specs/space/index.js` | full payload compatibility snapshots | +| `SPACE-R-005` accessibility | `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/lib/axe.js` | unit keyboard cases for all focus branches | +| `SPACE-R-006` teardown | `packages/node_modules/@webex/widget-space/src/reducer.test.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js` | repeated browser mount/remove leak test | + +## Traceability + +- Repo architecture: `../ARCHITECTURE.md`; registry: `../SPEC_INDEX.md`; contracts: `../CONTRACTS.md`. +- Coverage state, source routing, profiles, and contract baseline: `.sdd/manifest.json`. diff --git a/ai-docs/modules/state-management-spec.md b/ai-docs/modules/state-management-spec.md new file mode 100644 index 000000000..7ea75a727 --- /dev/null +++ b/ai-docs/modules/state-management-spec.md @@ -0,0 +1,247 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/state-management-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Redux and State Management — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and [`SERVICE_STATE.md`](../SERVICE_STATE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `state-management` | +| Source path(s) | `packages/node_modules/@webex/redux-module-*/`, `react-redux-spark*/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 94% assessed 2026-07-22; all state packages, public barrels, async conventions, and major invariants covered | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Reducer initial state, action constants/creators, thunks, tests, and widget composition define this module. View selectors live in widget or container packages where they exist. Redux state is client-side representation, never evidence that the repository owns durable Webex data. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Legacy `@ciscospark/redux-module-*` READMEs | namespace migration | verified | Export Stability records the suffix-preserving move to `@webex`. | +| Source barrels/actions/reducers | state contract | authoritative | Public Surface through State Model. | +| Adjacent reducer/action tests and widget/container selector tests | transitions | verified | Test-Case Strategy. | + +## Overview + +Twenty capability packages normalize Webex resources and async operations into Redux/Immutable.js state. `react-redux-spark` owns the SDK instance/authentication slice; feature modules own activities, conversation, spaces, users, media, meetings, presence, Mercury, flags, errors, and related projections. + +## Purpose / Responsibility + +Give widgets deterministic local state transitions around SDK calls and realtime events. This layer does not persist authoritative business data and must not invent server state. + +## Stack + +Redux 3, react-redux 5, redux-thunk, Immutable.js, reselect, recompose/React integration, Webex JS SDK, Jest, and redux-mock-store. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── redux-module-*/src/ +│ ├── actions.js +│ ├── reducer.js +│ └── index.js +├── react-redux-spark/src/ # SDK/auth state and enhancer +├── react-redux-spark-metrics/src/ # metrics state/actions +└── react-redux-spark-fixtures/src/ # test-only state fixtures +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/redux-module-activity/src/index.js`, `packages/node_modules/@webex/redux-module-spaces/src/index.js`, `packages/node_modules/@webex/redux-module-users/src/index.js` | representative public action/reducer barrels; every exact package path is indexed in `../CONTRACTS.md` | +| `packages/node_modules/@webex/redux-module-spaces/src/actions.js` | representative action types, creators, and async SDK work | +| `packages/node_modules/@webex/redux-module-spaces/src/reducer.js` | representative initial state and transitions | +| `packages/node_modules/@webex/react-redux-spark/src/index.js` | SDK Redux integration surface | +| `packages/node_modules/@webex/redux-module-meetings/src/actions.js` | meeting lifecycle bridge | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.state.modules` | SDK | `@webex/redux-module-*` barrels | capability reducers/actions/thunks | public semver; action/export changes require compatibility review; view selectors live in widget/container packages | exact `rw.state.*` catalog paths; representative: `packages/node_modules/@webex/redux-module-activity/src/index.js`, `packages/node_modules/@webex/redux-module-spaces/src/index.js` | `../CONTRACTS.md` | +| `rw.state.sdk` | SDK | `@webex/react-redux-spark*` barrels | SDK/auth, metrics, and fixture integration | public semver; fixtures remain test-oriented | `packages/node_modules/@webex/react-redux-spark/src/index.js`, `packages/node_modules/@webex/react-redux-spark-metrics/src/index.js`, `packages/node_modules/@webex/react-redux-spark-fixtures/src/index.js` | `../CONTRACTS.md` | + +Compatibility notes: + +The public state packages include activities, activity, avatar, conversation, errors, features, flags, indicators, media, meetings, Mercury, presence, search, share, spaces, teams, and users. Reducer keys are selected when widget entrypoints compose a store, rather than through one global root reducer. + +## Requires (dependencies) + +- Redux store/provider and thunk middleware assembled by the widget runtime. +- Webex SDK plugins for remote operations and Mercury events. +- Immutable.js-compatible state supplied to reducers and to widget/container selectors. +- Consumer widgets/containers to select and dispatch module behavior. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `STATE-R-001` | Each module exports a stable actions/reducer surface through its package barrel, plus optional constants, helpers, or thunks. View selectors live in consuming widget or container packages, not in `redux-module-*` barrels. | Widgets compose capability-specific stores and external packages import named operations. | `packages/node_modules/@webex/redux-module-activity/src/index.js`, `packages/node_modules/@webex/redux-module-spaces/src/index.js`, `packages/node_modules/@webex/redux-module-users/src/index.js` | `packages/node_modules/@webex/redux-module-activity/src/actions.test.js`, `packages/node_modules/@webex/redux-module-spaces/src/reducer.test.js` | Some modules export only a subset of those members. No `redux-module-*/src/selectors.js` files exist. | PRESENT | +| `STATE-R-002` | Async operations dispatch observable request/success/failure transitions around SDK promises. | UI needs deterministic loading and error state. | `packages/node_modules/@webex/redux-module-spaces/src/actions.js`, `packages/node_modules/@webex/redux-module-spaces/src/reducer.js` | `packages/node_modules/@webex/redux-module-spaces/src/actions.test.js`, `packages/node_modules/@webex/redux-module-spaces/src/reducer.test.js` | Exact status vocabulary varies by older module. | PRESENT | +| `STATE-R-003` | Remote resources are keyed/normalized and merged without replacing unrelated entities. | Realtime and request responses arrive incrementally. | `packages/node_modules/@webex/redux-module-activities/src/reducer.js`, `packages/node_modules/@webex/redux-module-spaces/src/reducer.js`, `packages/node_modules/@webex/redux-module-users/src/reducer.js` | `packages/node_modules/@webex/redux-module-spaces/src/reducer.test.js`, `packages/node_modules/@webex/redux-module-users/src/reducer.test.js` | Server conflict resolution remains SDK-owned. | PRESENT | +| `STATE-R-004` | Meetings store identifiers and readiness projections while the SDK meeting collection remains the live object authority. | SDK meeting/media instances are mutable event emitters unsuitable as canonical Redux data. | `packages/node_modules/@webex/redux-module-meetings/src/actions.js`, `packages/node_modules/@webex/redux-module-meetings/src/reducer.js` | `packages/node_modules/@webex/widget-meetings/src/components/MeetingsWidget.test.js` | Some lifecycle branches remain unimplemented. | PRESENT | +| `STATE-R-005` | Errors are exposed to views and reset when a new destination/operation begins where owning logic requests it. | Stale failures must not contaminate a new widget context. | `packages/node_modules/@webex/redux-module-errors/src/reducer.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.js` | `packages/node_modules/@webex/redux-module-errors/src/reducer.test.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js` | Reset ownership is distributed. | PRESENT | +| `STATE-R-006` | SDK auth and instance state are isolated in `react-redux-spark` and shared through its enhancers and connected consumers. | Avoid parallel SDK instances and inconsistent authentication state inside one widget. | `packages/node_modules/@webex/react-redux-spark/src/index.js`, `packages/node_modules/@webex/react-redux-spark/src/reducer.js` | `packages/node_modules/@webex/react-redux-spark/src/reducer.test.js`, `packages/node_modules/@webex/react-redux-spark/src/sdk.test.js` | Separate widgets may intentionally own separate stores. | PRESENT | + +## Design Overview + +Packages follow a small Redux module convention: constants/action creators and thunks produce actions; an Immutable reducer owns a slice; `index.js` exposes the supported boundary. View selectors, where present, live in widget or container packages. Widgets explicitly merge only required reducers, which keeps independently published packages composable. + +## Data Flow + +```mermaid +flowchart LR + UI[Widget/container] -->|dispatch| Thunk[Action/thunk] + Thunk -->|request| SDK[Webex SDK] + Thunk -->|request/success/failure| Reducer[Immutable reducer] + SDK -->|promise/event data| Thunk + Mercury[Mercury event] --> Reducer + Reducer --> State[Widget store] + State --> Selector[Widget/container selector] + Selector --> UI +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| dispatch an SDK-backed state operation | Async Redux transition | fulfilled and rejected branches | + +```mermaid +sequenceDiagram + participant V as View + participant A as Thunk + participant S as SDK + participant R as Reducer + V->>A: dispatch operation + A->>R: REQUEST + A->>S: invoke SDK + alt fulfilled + S-->>A: result + A->>R: SUCCESS + normalized payload + else rejected + S-->>A: error + A->>R: FAILURE + end + R-->>V: selected state +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class WidgetStore + class ThunkActions + class ImmutableReducer + class WidgetOrContainerSelectors + class WebexSDK + WidgetStore --> ThunkActions + ThunkActions --> WebexSDK + ThunkActions --> ImmutableReducer + ImmutableReducer --> WidgetStore + WidgetOrContainerSelectors --> WidgetStore +``` + +## Use Cases + +- Load and incrementally update spaces, users, activities, teams, presence, flags, and features. +- Send conversation operations while reflecting request/error state. +- Project SDK authentication/current-instance state into widget setup. +- Track meeting IDs/media readiness and retrieve live meetings from the SDK collection. +- Supply repeatable fixtures and metrics actions for tests/instrumentation. + +## State Model + +- Each reducer owns an Immutable map/list with explicit initial state. +- Common state dimensions are `items/byId`, current entity, operation status, errors, pagination, SDK instance/auth status, and feature/flag values. +- Triggers are widget dispatches, SDK promise outcomes, Mercury events, destination changes, and teardown/reset actions. +- Authoritative remote data remains Webex; Redux is a cache/projection scoped to the widget store. + +## Business Rules & Invariants + +- Reducers are pure and retain unrelated state for unknown actions. +- Entity IDs, not mutable SDK objects, are preferred when referencing live meetings/resources. +- A success/failure action corresponds to its owning request and preserves enough context for the view to decide recovery. +- Widget/container selectors do not mutate state and tolerate the initial/unloaded state expected by their consumers. They are not a `redux-module-*` barrel export. + +## Concurrency & Reactive Flow + +SDK promises and Mercury events can arrive out of order. Reducers merge by resource identity; setup/status guards prevent duplicate subscriptions/fetches. A destination change must reset scoped data or tag operations so late results cannot become the new visible destination. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Idle + Idle --> Requested: request action + Requested --> Ready: success + normalized payload + Requested --> Failed: failure action + Ready --> Requested: refresh/mutation + Failed --> Requested: retry/new context + Ready --> Idle: reset/teardown +``` + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| SDK promise rejects | module failure/error action | view renders error; owner retries or starts a new context | +| SDK is not authenticated/registered | auth/status slice not ready | wait for runtime readiness; do not dispatch dependent work | +| malformed/unrelated realtime payload | ignored or owning error action | retain existing state and inspect SDK/metrics logs | + +## Pitfalls + +- Similar modules use older, non-uniform status shapes; inspect the owning reducer before projecting state in a widget or container selector. +- Redux does not imply persistence or cross-widget sharing. +- Never serialize live SDK meeting/media objects into documentation or new reducer state without an explicit design change. + +## Module Do's / Don'ts + +- Do add action/reducer tests together for a new transition, and widget/container selector tests when those packages own the projection. +- Do compose reducers explicitly at the consuming widget boundary. +- Don't mutate Immutable state or SDK objects in reducers. +- Don't bypass exported barrels from another package without a documented internal reason. + +## Export Stability + +All non-private `@webex/redux-module-*` and `react-redux-spark*` entrypoints are public package contracts. Protected `@ciscospark` rename notices record the same-suffix move to `@webex`; maintain named action/reducer/helper compatibility under semver. + +## Key Design Trade-off + +Capability-local stores prevent a mandatory application-wide schema and support standalone widgets, at the cost of repeated reducer composition and potential duplicated SDK-derived cache state across widget instances. + +## Test-Case Strategy (module) + +| Requirement | Current evidence | Focused gap | +|---|---|---| +| `STATE-R-001` exports | `packages/node_modules/@webex/redux-module-activity/src/index.js`, `packages/node_modules/@webex/redux-module-activity/src/actions.test.js` | automated public-export snapshots | +| `STATE-R-002` async lifecycle | `packages/node_modules/@webex/redux-module-spaces/src/actions.test.js`, `packages/node_modules/@webex/redux-module-spaces/src/reducer.test.js` | cancellation/late-result tests | +| `STATE-R-003` normalized merge | `packages/node_modules/@webex/redux-module-users/src/reducer.test.js` | adversarial ordering | +| `STATE-R-004` meeting references | `packages/node_modules/@webex/redux-module-meetings/src/reducer.js` | destroyed/stopped event coverage | +| `STATE-R-005` error reset | `packages/node_modules/@webex/redux-module-errors/src/reducer.test.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js` | cross-destination late failure | +| `STATE-R-006` SDK auth | `packages/node_modules/@webex/react-redux-spark/src/sdk.test.js`, `packages/node_modules/@webex/react-redux-spark/src/reducer.test.js` | multi-widget isolation | + +## Traceability + +- System state boundaries: `../SERVICE_STATE.md`; contracts: `../CONTRACTS.md`. +- Redux conventions: `../patterns/redux-module-barrel.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/test-automation-spec.md b/ai-docs/modules/test-automation-spec.md new file mode 100644 index 000000000..0d9872c1b --- /dev/null +++ b/ai-docs/modules/test-automation-spec.md @@ -0,0 +1,272 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/test-automation-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Test Automation — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and repository [`REVIEW_CHECKLIST.md`](../REVIEW_CHECKLIST.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `test-automation` | +| Source path(s) | adjacent `*.test.js`, `test/journeys/`, `jest.config.json`, `wdio.conf.js`, `scripts/tests/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 96% assessed 2026-07-22; unit/journey topology, protected plan intent, suites, environment, fixtures, and CI use covered | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Executable Jest/WDIO configuration and test code determine current coverage. The protected journey test plan preserves intended scenarios; where prose and executable suites diverge, this spec records the gap instead of claiming a pass. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Protected journey plan | journey intent | verified/reconciled | Suite inventory and scenario expectations are preserved below and mapped to executable specs. | +| Repository usage-guide test commands | developer workflow | verified | Public Surface and Use Cases. | +| Jest/WDIO/CircleCI configuration | execution | authoritative | Requirements, Design Overview, Error Handling. | +| adjacent tests and journey specs/helpers | behavior | authoritative | Test-Case Strategy. | + +## Overview + +The repository uses adjacent Jest suites for package-level units and WebdriverIO journeys for real browser/widget integration. Journeys cover smoke, Space, Recents, production TAP, data API, browser-global, multiple-widget, guest, startup-setting, event, media, and accessibility behavior. CircleCI runs lint/Jest and configured Chrome/Firefox integration jobs. + +## Purpose / Responsibility + +Detect regressions in independently published packages and embedded widget behavior, while keeping external-service requirements and evidence limits explicit. + +## Stack + +Jest 24, Babel setup, identity/file/sound mocks, react-test-renderer, redux-mock-store, WebdriverIO 7/Mocha/Chai, Sauce Labs or local Selenium, static server service, Webex test-user helpers, Axe, CircleCI, Chrome, and Firefox. + +## Folder / Package Structure + +```text +jest.config.json +packages/node_modules/@webex/**/src/*.test.js +scripts/tests/ # setup, async/openh264, suite preparation +test/journeys/ +├── specs/ +│ ├── smoke/ +│ ├── space/ +│ ├── recents/ +│ └── tap/ +├── lib/ # browser, users, events, helpers, waiters, axe +├── server/ # local/global/data-api pages +└── testplan.md # protected intent source +wdio.conf.js +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `jest.config.json` | unit roots, transforms, aliases, mocks, setup | +| `scripts/tests/jest-setup.js` | global test environment | +| `wdio.conf.js` | suites, browsers, Sauce/local services, timeouts, reporters | +| `test/journeys/specs/**/*.js` | executable integration assertions | +| `test/journeys/lib/test-helpers/` | widget actions/assertion helpers | +| `test/journeys/testplan.md` | protected human scenario inventory | +| `.circleci/config.yml` | CI test invocation and artifacts | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.cmd.static-analysis` | CLI | `npm run static-analysis`, `npm run eslint` | repository lint gate | CI/developer contract | `package.json` | `../CONTRACTS.md` | +| `rw.cmd.jest` | CLI | `npm run jest`, `npm test` | package units; combined lint/unit gate | CI/developer contract | `package.json`, `jest.config.json` | `../CONTRACTS.md` | +| `rw.cmd.journeys` | CLI | `npm run test:automation[:smoke|:space|:recents]` | local/remote browser suites | suite names stable for CI | `package.json`, `wdio.conf.js` | `../CONTRACTS.md` | +| `rw.cmd.tap` | CLI | `npm run test:tap`, `npm run test:integration` | production TAP or smoke integration | explicit environment/target required | `package.json`, `wdio.conf.js` | `../CONTRACTS.md` | + +Compatibility notes: + +WDIO accepts `BROWSER`, `VERSION`, `PLATFORM`, `SAUCE`, `TAP`, `INTEGRATION`, `JOURNEY_TEST_BASE_URL`, `STATIC_SERVER_PATH`, `BUILD_NUMBER`, test-user/service variables, and Sauce credentials. + +## Requires (dependencies) + +Installed npm dependencies and built widget distributions; Chrome/Firefox plus local Selenium or Sauce credentials; Webex test users and integration endpoints for remote journeys; fake media device settings; writable report/artifact locations. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `TEST-R-001` | Jest discovers adjacent `*.test.js` only under tracked package source and maps `@webex`/legacy aliases to source. | Unit tests must exercise repository code, not published builds. | `jest.config.json` | `packages/node_modules/@webex/widget-space/src/reducer.test.js`, `packages/node_modules/@webex/widget-recents/src/enhancers/setup.test.js` | TypeScript-named tests outside regex need explicit support. | PRESENT | +| `TEST-R-002` | Smoke verifies Space, Recents, multiple widgets, demo auth modes, core messaging/calling, events, and accessibility. | Every PR needs a bounded integration signal across primary embeddings. | `wdio.conf.js`, `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/specs/smoke/widget-recents/index.js`, `test/journeys/specs/smoke/multiple/index.js`, `test/journeys/specs/smoke/demo.js` | same executable smoke specs | Calling breadth is limited to composed Space behavior. | PRESENT | +| `TEST-R-003` | Space journeys cover global/data API, messaging/actions/files/markdown, roster, call lifecycle, guest access, startup settings, events, and accessibility. | Space is a primary embeddable product surface. | `test/journeys/specs/space/index.js`, `test/journeys/specs/space/guest.js`, `test/journeys/specs/space/startup-settings.js`, `test/journeys/specs/space/data-api.js` | same executable Space specs | External services/test users can cause non-product failures. | PRESENT | +| `TEST-R-004` | Recents journeys cover global/data API, group/one-to-one updates, unread/read/select/member events, filters, startup settings, incoming-call indicators, and accessibility. | Recents must react correctly to SDK-driven changes in both host APIs. | `test/journeys/specs/recents/dataApi/basic.js`, `test/journeys/specs/recents/global/basic.js`, `test/journeys/specs/recents/dataApi/space-list-filter.js`, `test/journeys/specs/recents/global/startup-settings.js` | same executable Recents specs | Some plan wording predates current filters/settings specs. | PRESENT | +| `TEST-R-005` | Browser runs use fake media/notification settings, bounded waits, prepared test users, and stored diagnostics/reports. | Realtime/media UI requires reproducible automation and debuggable failures. | `wdio.conf.js`, `scripts/tests/beforeSuite.js`, `test/journeys/lib/wait-for-mercury-event.js`, `test/journeys/lib/axe.js` | `.circleci/config.yml` | Network timing remains nondeterministic. | PRESENT | +| `TEST-R-006` | Contributors run focused full widget journeys before PR; CI runs configured smoke/integration plus lint/Jest. | Local ownership and CI gates divide expensive coverage responsibly. | `test/journeys/testplan.md`, `.circleci/config.yml` | `package.json` | CI workflow conditions require independent check. | PRESENT | + +## Design Overview + +Unit suites stay beside implementation and mock styles/assets/dependencies through Jest. Browser journeys drive static test pages and Webex APIs using reusable helpers, waiting for SDK/Mercury outcomes rather than only DOM timing. WDIO selects suite/environment and records JUnit/browser artifacts; CI builds distributions before journeys. + +## Data Flow + +```mermaid +flowchart LR + Source[Package source] --> Jest[Jest + mocks] + Source --> Build[Journey bundles] + Build --> Static[Static test server] + Static --> WDIO[WDIO browser] + Helpers[Test users/SDK helpers] --> WDIO + Webex[Integration services] --> WDIO + Jest --> Reports[Results] + WDIO --> Reports + Reports --> CI[CI gate/artifacts] +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| deterministic package units | Jest source suite | transform/setup/assertion failure blocks the command | +| built browser journeys | WDIO integration suite | build/auth/setup/assertion failures retain diagnostics and block CI | + +```mermaid +sequenceDiagram + participant D as Developer/CI + participant J as Jest + participant C as Jest config/setup + participant S as Package source/tests + D->>J: npm run jest + J->>C: load roots, transforms, aliases, mocks + C->>S: discover and execute .test.js suites + alt all assertions pass + S-->>D: success + else setup/transform/assertion failure + S-->>D: failed suite and diagnostics + end +``` + +```mermaid +sequenceDiagram + participant C as CI/developer + participant B as Build/static server + participant W as WDIO browser + participant X as Webex test services + C->>B: build selected widget distributions + C->>W: start suite with environment + W->>B: load global/data/demo test page + W->>X: prepare users and trigger SDK behavior + X-->>W: API/Mercury/call result + W-->>C: assertion, JUnit, screenshots/logs + alt assertion/setup failure + C-->>C: retain diagnostics and block gate + end +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class JestConfig + class AdjacentUnitTests + class WdioConfig + class JourneySpecs + class JourneyHelpers + class StaticPages + class CircleCI + JestConfig --> AdjacentUnitTests + WdioConfig --> JourneySpecs + JourneySpecs --> JourneyHelpers + JourneySpecs --> StaticPages + CircleCI --> JestConfig + CircleCI --> WdioConfig +``` + +## Use Cases + +- Run all deterministic package suites with UTC timezone and repository source aliases. +- Run smoke locally against built distributions before opening a PR. +- Run the full Space or Recents suite for a changed primary widget. +- Exercise production assets through TAP without starting the local static server. +- Diagnose a failed remote browser run from JUnit, browser artifacts, build identity, and Sauce session. + +Protected-plan scenario inventory: + +- **Recents smoke:** group and one-to-one incoming messages, read/unread behavior, new one-to-one, call indicators, created/read/unread/selected/member events, and Axe. +- **Space smoke:** activity-menu open/close and Message/Meet/Files/Roster switches, roster count/list/close, send/receive, call/hangup, and Axe. +- **Multiple/demo smoke:** Space and Recents on one page; demo access-token and SDK-instance authentication; external activity control. +- **Space full:** header/menu/roster/search/add; send/receive/events; flag/unflag; self-delete/no other-delete; PNG/file tab; bold, italic, quote, lists, H1-H3, horizontal rule, link, inline code, and code block. +- **Space call/guest/settings/data API:** precall, hangup before answer, decline, hangup, call payload; guest messaging/calls; `userId`, disabled activities/error, initial Meet/Message, start-call; both data and global instantiation. +- **Recents full:** data/global group and one-to-one update/read/call-hover; new one-to-one; incoming-call progress; current events; filters/startup settings; Axe. + +## Business Rules & Invariants + +- A test claiming a current event uses a constant/runtime event still present in source. +- Integration tests prepare/clean isolated users/spaces and wait for observable Webex outcomes. +- Tests never commit credentials, access tokens, or Sauce secrets. +- Accessibility assertions are required in the existing smoke/full surfaces and are not replaced by snapshot tests. + +## Concurrency & Reactive Flow + +Mercury, SDK promises, call/media setup, browsers, and remote test-user services are asynchronous. Helpers use bounded waits and event synchronization; arbitrary sleeps are a last resort. Parallel sessions require unique build/tunnel/user context and must not share mutable conversations unintentionally. + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| setup/build/auth failure | before-suite or WDIO setup failure | fix environment/asset/user preparation before interpreting product assertions | +| assertion/timeout | failed Jest/WDIO result with reports/artifacts | inspect event timing, browser artifacts, and build identity | +| local static assets missing | page/load failure | build journey/widget assets or configure a remote base URL | +| repeated environmental flake | reproducible intermittent failure | assign owner/expiry and fix synchronization; do not hide with retries | + +## Pitfalls + +- The protected plan describes intent, not proof that every scenario still executes. +- Jest `testRegex` is `.test.js$`; TypeScript test naming requires validation. +- Browser journeys depend on mutable external services and credentials. +- Smoke is not a substitute for the changed widget's full suite. + +## Module Do's / Don'ts + +- Do add the narrowest deterministic unit test and the relevant journey for public behavior. +- Do use event/wait helpers and preserve diagnostics. +- Don't weaken assertions to accommodate timing without proving the race. +- Don't run production TAP against unapproved targets or expose credentials in logs. + +## Key Design Trade-off + +Real Webex/browser journeys give high-confidence integration evidence but are expensive and environment-sensitive, so adjacent unit tests provide fast breadth while focused full suites and CI smoke provide layered assurance. + +## Test-Case Strategy (module) + +| Layer | Scope | Expected gate | +|---|---|---| +| Static analysis | all tracked JS/TS/config/docs according to ESLint ignores | `npm run static-analysis` passes | +| Jest | 107 adjacent `*.test.js` suites discovered under `packages/node_modules/@webex/` | `npm run jest` passes | +| Smoke integration | Space, Recents, multiple, demo | required CI integration signal | +| Focused Space | messaging/files/roster/calls/guest/settings/data API | run for Space-related changes | +| Focused Recents | global/data API/filter/settings/events | run for Recents-related changes | +| TAP | production-hosted selected widget flows | explicit production validation | +| Accessibility | Axe in protected smoke/full surfaces | zero blocking violations | + +| Requirement | Existing test/config evidence | Focused gap | +|---|---|---| +| `TEST-R-001` Jest discovery | `jest.config.json`, `packages/node_modules/@webex/widget-space/src/reducer.test.js` | TypeScript test discovery | +| `TEST-R-002` smoke | `wdio.conf.js`, `test/journeys/specs/smoke/widget-space/index.js`, `test/journeys/specs/smoke/widget-recents/index.js` | dedicated calling widgets | +| `TEST-R-003` Space | `test/journeys/specs/space/index.js`, `test/journeys/specs/space/guest.js`, `test/journeys/specs/space/data-api.js` | remote-service failure isolation | +| `TEST-R-004` Recents | `test/journeys/specs/recents/global/basic.js`, `test/journeys/specs/recents/dataApi/basic.js` | stale-event negative assertions | +| `TEST-R-005` browser setup | `scripts/tests/beforeSuite.js`, `test/journeys/lib/wait-for-mercury-event.js` | bounded wait consistency | +| `TEST-R-006` contributor/CI gates | `test/journeys/testplan.md`, `.circleci/config.yml` | automated plan-to-suite drift check | + +Coverage gaps to retain: dedicated calling-widget journeys are sparse; several TypeScript calling components have stories rather than unit suites; teardown/race/error paths need focused characterization. + +## Traceability + +- Quality gate commands: `../GETTING_STARTED.md`, `../REVIEW_CHECKLIST.md`. +- Capability requirement mappings: every file in `ai-docs/modules/`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/modules/widget-runtime-auth-spec.md b/ai-docs/modules/widget-runtime-auth-spec.md new file mode 100644 index 000000000..dbb0ce9e0 --- /dev/null +++ b/ai-docs/modules/widget-runtime-auth-spec.md @@ -0,0 +1,267 @@ +<!-- ─────────────────────────────── + Template: Module Spec + Template-ID: module-spec + Generates: ai-docs/modules/widget-runtime-auth-spec.md + Description: Per-module canonical spec — orientation plus requirements, design, invariants, flows, pitfalls, and tests. + Library ver: 0.2.2 + Last updated: 2026-07-30 +─────────────────────────────── --> + +# Widget Runtime, Authentication, and Demos — SPEC + +> Start with root [`AGENTS.md`](../../AGENTS.md), router [`SPEC_INDEX.md`](../SPEC_INDEX.md), and system [`ARCHITECTURE.md`](../ARCHITECTURE.md). + +## Metadata + +| Field | Value | +|---|---| +| Module id | `widget-runtime-auth` | +| Source path(s) | `packages/node_modules/@webex/webex-widget-base/`, `webex-sign-in-page/`, `widget-*-demo/`, `samples/` | +| Parent spec | — | +| Doc kind | Module spec | +| Coverage score | 93% assessed 2026-07-22; enhancer order, host APIs, auth, teardown, and demo boundaries covered | +| Generated from | `module-spec` @ SDLC template library `0.2.2` | +| generated_by / approved_by / updated_at | `codex-desktop` / pending PR approval / 2026-08-07 | +| Validation status | pass, validator `codex-desktop`, assessed 2026-09-03; 0 Blocking, 0 warnings; source `548a0312` | + +## Evidence Rules + +Current enhancer signatures/order, package exports, host API implementation, tests, and demo wiring are authoritative. The protected widget-base README is reconciled: its two-argument default quick start conflicts with current implementation and is not promoted. + +## Source Material Register + +| Source material | Scope | Decision | Detail location or disposition | +|---|---|---|---| +| Protected base usage guidance | enhancer and host usage | corrected | Object-shaped `constructWebexEnhancer` is the supported source-backed construction path; conflict is in Pitfalls. | +| Protected demo guidance | demo startup | verified | Use Cases and Host Integration. | +| Repository usage guidance | browser/data API usage | verified/expanded | Current runtime surfaces are in Public Surface; its limited widget list remains unchanged. | +| legacy rename READMEs | namespace migration | verified | Export Stability. | + +## Overview + +`webex-widget-base` turns a React component into an embeddable widget by composing data API registration, browser globals, a Redux store, removal, SDK authentication, current-user loading, display name, and version metadata. The sign-in and demo/sample packages provide development/host entrypoints around this runtime. + +## Purpose / Responsibility + +Own widget instantiation, per-widget store/SDK/auth wiring, browser/data-attribute host APIs, localization hooks, removal, and developer demo shells. Capability widgets own domain behavior. + +## Stack + +React/ReactDOM, recompose, Redux/react-redux, Webex JS SDK, react-intl, ampersand-events, CustomEvent, Babel/Webpack development runtime, TypeScript for sign-in UI, and Jest/WebdriverIO. + +## Folder / Package Structure + +```text +packages/node_modules/@webex/ +├── webex-widget-base/src/ +│ └── enhancers/ # data API, browser globals, store, removal, intl, current user +├── webex-sign-in-page/src/ +├── widget-demo/src/ # executable shared demo shell +├── widget-space-demo/src/ # index.html only; package src entry is absent +└── widget-recents-demo/src/ # index.html only; package src entry is absent +samples/ # host examples served by tooling +``` + +## Key Files (source of truth) + +| File | Holds | +|---|---| +| `packages/node_modules/@webex/webex-widget-base/src/index.js` | canonical enhancer composition/order and exports | +| `packages/node_modules/@webex/webex-widget-base/src/enhancers/withDataAPI.js` | data-attribute discovery/instantiation | +| `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | global registry, lookup, host events | +| `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js` | per-widget Redux store; Provider skipped when `props.spark` is supplied | +| `packages/node_modules/@webex/webex-widget-base/src/enhancers/withRemoveWidget.js` | injected `REMOVE_WIDGET` action; browser `remove()` does not dispatch it | +| `packages/node_modules/@webex/webex-widget-base/src/enhancers/withCurrentUser.js` | authenticated user loading | +| `packages/node_modules/@webex/webex-sign-in-page/src/index.ts` | sign-in page public barrel | +| `packages/node_modules/@webex/widget-demo/src/index.js` | development demo mount | + +## Public Surface + +| Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index | +|---|---|---|---|---|---|---| +| `rw.runtime.base` | SDK/React | `constructWebexEnhancer({name, reducers, enhancers})`, default `WebexWidgetBase(name, BaseComponent)`, `withIntl`, `withInitialState`, `withBrowserGlobals` | common runtime composition | public semver; `enhancers` is accepted/documented by the entrypoint but ignored by `withInitialState`; the default helper has the signature mismatch below | `packages/node_modules/@webex/webex-widget-base/src/index.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js` | `../CONTRACTS.md` | +| `rw.host.widget.select` | browser API | `window.webex.widget(element)` | select/create a browser widget | stable global/alias and lookup behavior | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | `../CONTRACTS.md` | +| `rw.host.widget.mount` | browser API | `widget.{name}Widget(options)` | mount a registered capability | names/options are public | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | `../CONTRACTS.md` | +| `rw.host.widget.remove` | browser API | `widget.remove(callback?)` | unmount React and delete the UUID registry entry | always returns a Promise; no-callback form resolves to the unmount boolean; callback form invokes the callback and leaves the Promise pending; no Redux reset is dispatched | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js` | `../CONTRACTS.md` | +| `rw.host.data-api` | data API | `[data-toggle^="webex-{name}"]` + `data-*` | discover and mount widgets | names and kebab-to-camel mapping are public | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withDataAPI.js` | `../CONTRACTS.md` | +| `rw.runtime.sign-in` | SDK/React | `webex-sign-in-page` typed barrel | reusable sign-in UI | public semver | `packages/node_modules/@webex/webex-sign-in-page/src/index.ts` | `../CONTRACTS.md` | + +Compatibility notes: + +Data API hosts instantiate supported widget elements/attributes after runtime discovery. + +The default `WebexWidgetBase(name, BaseComponent)` export currently passes `name` to an object-destructuring function; consumers must not treat the protected README example as validated until that implementation conflict is resolved. + +## Requires (dependencies) + +React DOM, Redux reducer maps supplied by each widget, capability HOCs composed outside the base constructor, Webex SDK credentials or instance, browser globals/CustomEvent support for embedded APIs, locale/intl data, and bundler-injected `REACT_WEBEX_VERSION`. + +## Requirements + +| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence | +|---|---|---|---|---|---|---| +| `RUNTIME-R-001` | Enhancers execute in the declared compose order: data API, browser globals, initial state, removal, SDK, current user, display name, version. | Outer/inner ordering controls host discovery, store availability, auth, and teardown. | `packages/node_modules/@webex/webex-widget-base/src/index.js` | `test/journeys/specs/smoke/multiple/index.js` | No direct base composition test; reordering requires characterization. | PRESENT | +| `RUNTIME-R-002` | The default `withInitialState` path creates a per-widget store and wraps the widget in `<Provider>`. When `props.spark` is supplied, `withInitialState` still constructs a store but skips Provider injection, so that store is not provided to the widget. The constructor's `enhancers` field has no effect because `withInitialState` ignores it. | Hosts need the actual store/Provider boundary, and consumers must not rely on a documented-but-unused option. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js`, `packages/node_modules/@webex/webex-widget-base/src/index.js` | None found for store isolation. | The multiple-widget journey proves coexistence, not store-object isolation. The `props.spark` skip path needs characterization. | PRESENT | +| `RUNTIME-R-003` | Browser/data APIs discover, register, look up, emit from, and remove widgets using stable names/UUIDs. | Non-React hosts depend on embedding contracts. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withDataAPI.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withRemoveWidget.js` | `test/journeys/specs/space/data-api.js`, `test/journeys/specs/smoke/multiple/index.js` | Exact DOM payloads require independent validation. | PRESENT | +| `RUNTIME-R-004` | SDK-dependent child setup waits for valid auth/registration/current-user state. | Prevent unauthenticated remote calls and incomplete identity UI. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withCurrentUser.js`, `packages/node_modules/@webex/react-redux-spark/src/index.js` | `packages/node_modules/@webex/react-redux-spark/src/sdk.test.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js` | Credentials are environment-owned. | PRESENT | +| `RUNTIME-R-005` | Browser `remove()` unmounts React and deletes the UUID registry entry. It always returns a Promise. When a function callback is supplied, the callback receives the unmount boolean and the Promise remains pending; when no callback is supplied, the Promise resolves to that boolean. It does not dispatch `REMOVE_WIDGET` or explicitly reset Redux. | Embedded hosts need the exact implemented teardown boundary and must not assume a settled Promise on the callback path or a state-reset side effect. | `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withRemoveWidget.js`, `packages/node_modules/@webex/webex-widget-base/src/enhancers/withInitialState.js` | None found for browser removal semantics. | The pending-Promise callback branch is current behavior and needs characterization tests; child unmount cleanup is distributed; repeated-remove and store-retention behavior need characterization. | PRESENT | +| `RUNTIME-R-006` | Demo/sample packages remain development shells and are not published as production capability contracts when marked private. | Avoid treating example configuration/token handling as supported API. | `packages/node_modules/@webex/widget-demo/package.json`, `packages/node_modules/@webex/widget-demo/src/index.js`, `scripts/start/commands/demo.js` | `test/journeys/specs/smoke/demo.js` | Some public sign-in UI is shared with demos. | PRESENT | + +## Design Overview + +The runtime is an enhancer pipeline around a capability component. Store and SDK concerns are injected below host-facing wrappers, allowing browser and data APIs to instantiate the same React export. Widgets provide reducer maps; capability-specific HOCs are composed around the base enhancer in their own entrypoints. Although the base constructor accepts an `enhancers` field, the current store initializer ignores it. `withInitialState` always constructs a store, but it wraps with `<Provider>` only when `props.spark` is absent; an externally supplied SDK/spark instance skips that Provider. Demos mount packaged widgets in development-only shells. + +## Data Flow + +```mermaid +flowchart LR + Host[React/browser/data host] --> Registry[Data API + browser globals] + Registry --> DefaultStore[Default per-widget Provider] + Registry --> SparkPath[props.spark skips Provider] + DefaultStore --> Auth[Webex SDK/auth] + SparkPath --> Auth + Auth --> User[Current-user load] + User --> Widget[Capability component] + Widget --> Events[Callbacks/CustomEvents] + Events --> Host + Host --> Remove[remove contract] + Remove --> Registry +``` + +## Sequence Diagram(s) + +Sequence coverage: + +| Operation group | Diagram | Failure / recovery coverage | +|---|---|---| +| discover, register, authenticate, render, emit, remove | Widget instance lifecycle | auth/lookup/removal failures and the setup/removal race are specified in Error Handling | + +```mermaid +sequenceDiagram + participant H as Host + participant B as Base runtime + participant R as Registry/store + participant S as Webex SDK + participant W as Widget + H->>B: instantiate name + options/element + alt no props.spark + B->>R: allocate UUID and wrap with per-widget Provider + else props.spark supplied + B->>R: allocate UUID; skip Provider injection + end + B->>S: create/use SDK and authenticate + S-->>B: authenticated/current user + B->>W: render injected props + W-->>H: callback/DOM event + H->>B: remove + B->>R: unmount and clear registry entry (no Redux reset dispatch) +``` + +## Class / Component Relationships + +```mermaid +classDiagram + class HostAPI + class ConstructWebexEnhancer + class ReduxStore + class WebexSDK + class CapabilityWidget + class DemoShell + HostAPI --> ConstructWebexEnhancer + ConstructWebexEnhancer --> ReduxStore + ConstructWebexEnhancer --> WebexSDK + ConstructWebexEnhancer --> CapabilityWidget + DemoShell --> HostAPI +``` + +## Use Cases + +- Embed a packaged widget directly as React. +- Discover and instantiate a widget from supported DOM/data attributes. +- Look up a widget instance, listen for events, and remove it from a non-React host. +- Authenticate/use a Webex SDK instance and load the current user before capability setup. +- Run the local demo or sample server to exercise Space/Recents integrations. + +## State Model + +The runtime tracks a per-widget Redux store on the default Provider path, SDK/auth state, current-user fetch state, widget UUID/registry entry, and removal lifecycle. When `props.spark` is supplied, that constructed store is not injected. Inputs are host properties/attributes, credentials/SDK instance, reducer maps, locale, and DOM mount points. Capability entrypoints compose additional HOCs outside the base constructor. + +## Business Rules & Invariants + +- `withDataAPI` remains outermost so host discovery wraps the completed widget. +- Store initialization precedes SDK/current-user consumers. +- Widget names and UUID registry keys are stable within an instance lifecycle. +- Removal is idempotent from a host perspective and must not leave a usable stale registry entry. + +## Concurrency & Reactive Flow + +DOM discovery, SDK authentication, current-user fetches, widget events, and removal are asynchronous. A removal racing setup must prevent late results from re-registering or updating an unmounted instance. + +## State Machine + +```mermaid +stateDiagram-v2 + [*] --> Discovered + Discovered --> Registered: allocate registry/store + Registered --> Authenticating + Authenticating --> Ready: SDK + current user ready + Authenticating --> Error: auth/user failure + Ready --> Removing: host remove + Error --> Removing + Removing --> Removed +``` + +## UI Flow + +Hosts see discovery/mount, authentication/loading or sign-in UI, the capability widget, and error/removal states. Demo UI supplies credentials/configuration and must not expose production secrets or redefine widget behavior. + +## Error Handling & Failure Modes + +| Condition | Signal (error/code/result) | Caller recovery | +|---|---|---| +| authentication/current-user failure | SDK/error state; capability remains unready | correct credentials or retry through owning host | +| invalid host element/name | discovery/lookup does not return a valid instance | correct mount markup/name and instantiate again | +| browser unmount returns `false` | callback receives `false`; Promise resolves `false` only on the no-callback path; UUID entry is still deleted | inspect the host element; do not await the callback-path Promise or assume Redux reset occurred | +| remove races setup | late async result after removal | ignore/cancel result; do not re-register instance | + +## Pitfalls + +- Protected base documentation shows `WebexWidgetBase(name, Component)`, but current default implementation conflicts with `constructWebexEnhancer`'s object signature. Prefer the named object-shaped constructor pending a code/API decision. +- Global registries make tests order-sensitive unless reset. +- Demo token handling is development guidance, not a production authentication design. + +## Module Do's / Don'ts + +- Do preserve enhancer order and characterize it before edits. +- Do treat widget removal and listener cleanup as one lifecycle. +- Don't put capability reducers into the base package. +- Don't promote private demo/sample APIs as supported exports. + +## Export Stability + +`@webex/webex-widget-base` and `@webex/webex-sign-in-page` are public. Protected `@ciscospark` notices record namespace migration for legacy runtime packages. Browser/data API names, emitted event translation, and removal behavior are compatibility surfaces as well as JavaScript exports. + +## Host Integration & Theming + +Hosts may use React, browser globals, or data attributes. They provide DOM roots, credentials/SDK/options, locale, and event listeners; the runtime supplies base fonts/styles, version metadata, and Redux/provider context on the default path. An externally supplied `spark` prop skips that Provider. Multiple widgets must coexist without ID/store collisions. + +## Key Design Trade-off + +One enhancer pipeline makes independently packaged widgets consistently embeddable, but ordering and global-registration behavior create implicit coupling. The canonical object-shaped constructor and lifecycle tests are the guardrails. + +## Test-Case Strategy (module) + +| Requirement | Current evidence | Focused gap | +|---|---|---| +| `RUNTIME-R-001` order | `packages/node_modules/@webex/webex-widget-base/src/index.js`; no direct unit test found | explicit composition-order characterization | +| `RUNTIME-R-002` isolation | None found | concurrent store/SDK identity assertions, unused-`enhancers` characterization, and `props.spark` Provider-skip | +| `RUNTIME-R-003` host APIs | `test/journeys/specs/space/data-api.js`, `test/journeys/specs/smoke/multiple/index.js` | invalid lookup and repeated remove | +| `RUNTIME-R-004` auth | `packages/node_modules/@webex/react-redux-spark/src/sdk.test.js`, `packages/node_modules/@webex/widget-space/src/enhancers/setup.test.js` | auth rejection/recovery | +| `RUNTIME-R-005` teardown | None found | callback-path pending Promise, no-callback resolve, unmount/registry assertions, explicit no-reset characterization, setup/remove race, and listener audit | +| `RUNTIME-R-006` demos | `test/journeys/specs/smoke/demo.js` | production-build exclusion | + +## Traceability + +- Runtime/contract overview: `../ARCHITECTURE.md`, `../CONTRACTS.md`. +- Composition pattern and cleanup rule: `../patterns/widget-enhancer-composition.md`, `../rules/clean-up-runtime-listeners.md`. +- Machine coverage/profile/contracts: `.sdd/manifest.json`. diff --git a/ai-docs/patterns/README.md b/ai-docs/patterns/README.md new file mode 100644 index 000000000..d184e1074 --- /dev/null +++ b/ai-docs/patterns/README.md @@ -0,0 +1,13 @@ +# Repository patterns + +Code-grounded conventions that appear in at least three real files and are not fully explained by ESLint. Use the example files here before adding a component, Redux module, or widget entrypoint. + +## Use Patterns For + +- Choosing the established package-entrypoint, Redux-barrel, and widget-enhancer shapes for new or changed code. +- Comparing a proposed implementation with correct and incorrect repository-grounded examples. +- Finding the concrete source paths from which each convention was extracted. + +- `react-component-entrypoint.md` — PropTypes/defaults/component/default-export shape. +- `redux-module-barrel.md` — actions plus reducer/initial-state package surface. +- `widget-enhancer-composition.md` — widget entrypoints composed through the shared runtime. diff --git a/ai-docs/patterns/react-component-entrypoint.md b/ai-docs/patterns/react-component-entrypoint.md new file mode 100644 index 000000000..259a32098 --- /dev/null +++ b/ai-docs/patterns/react-component-entrypoint.md @@ -0,0 +1,51 @@ +<!-- ─────────────────────────────── + Template: Pattern (example) + Template-ID: pattern + Generates: ai-docs/patterns/react-component-entrypoint.md + Description: A repo convention from real code — correct vs incorrect form, with where it appears. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Pattern: React component entrypoint + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md). + +## When to use + +**When to use:** adding or maintaining a legacy JavaScript `react-component-*` package whose public entrypoint is the component itself. + +## Correct + +```js +// Shape used by packages/node_modules/@webex/react-component-avatar/src/index.js +const propTypes = {/* public props */}; +const defaultProps = {/* optional defaults */}; + +function Component(props) {/* render */} + +Component.propTypes = propTypes; +Component.defaultProps = defaultProps; +export default Component; +``` + +## Incorrect + +```js +// An untyped anonymous default with hidden defaults +export default (props) => renderSomething(props); +``` + +**Why wrong:** consumers/tests lose the explicit prop/default contract and debugging display identity; adjacent packages cannot follow a consistent entrypoint shape. + +## Where it appears + +- `packages/node_modules/@webex/react-component-avatar/src/index.js` +- `packages/node_modules/@webex/react-component-button/src/index.js` +- `packages/node_modules/@webex/react-component-error-display/src/index.js` +- `packages/node_modules/@webex/react-component-textarea/src/index.js` + +## Edge cases / exceptions + +- TypeScript calling components use exported interfaces/types rather than PropTypes. +- Connected components may export a named unconnected implementation and a wrapped default. diff --git a/ai-docs/patterns/redux-module-barrel.md b/ai-docs/patterns/redux-module-barrel.md new file mode 100644 index 000000000..a7318e6d4 --- /dev/null +++ b/ai-docs/patterns/redux-module-barrel.md @@ -0,0 +1,45 @@ +<!-- ─────────────────────────────── + Template: Pattern (example) + Template-ID: pattern + Generates: ai-docs/patterns/redux-module-barrel.md + Description: A repo convention from real code — correct vs incorrect form, with where it appears. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Pattern: Redux module barrel + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md). + +## When to use + +**When to use:** exposing a legacy Redux capability package to widgets/containers. + +## Correct + +```js +// Shape used by packages/node_modules/@webex/redux-module-spaces/src/index.js +export * from './actions'; +export {default, initialState} from './reducer'; +``` + +## Incorrect + +```js +// Consumers reach into internal files and bypass the package contract +import reducer from '@webex/redux-module-spaces/src/reducer'; +``` + +**Why wrong:** deep imports couple consumers to internal layout and make action/reducer/export changes harder to version safely. + +## Where it appears + +- `packages/node_modules/@webex/redux-module-spaces/src/index.js` +- `packages/node_modules/@webex/redux-module-users/src/index.js` +- `packages/node_modules/@webex/redux-module-teams/src/index.js` +- `packages/node_modules/@webex/redux-module-conversation/src/index.js` + +## Edge cases / exceptions + +- Existing repository-internal deep imports are legacy exceptions; do not create new ones without a boundary rationale. +- Small modules such as features/search implement reducer/actions together in `index.js` but retain the same public concepts. diff --git a/ai-docs/patterns/widget-enhancer-composition.md b/ai-docs/patterns/widget-enhancer-composition.md new file mode 100644 index 000000000..798263db5 --- /dev/null +++ b/ai-docs/patterns/widget-enhancer-composition.md @@ -0,0 +1,47 @@ +<!-- ─────────────────────────────── + Template: Pattern (example) + Template-ID: pattern + Generates: ai-docs/patterns/widget-enhancer-composition.md + Description: A repo convention from real code — correct vs incorrect form, with where it appears. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Pattern: Widget enhancer composition + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md). + +## When to use + +**When to use:** creating or maintaining a legacy Webex widget that needs the shared browser/data API, Redux, SDK/auth, current-user, teardown, intl, and version behavior. + +## Correct + +```js +// Shape used by packages/node_modules/@webex/widget-space/src/index.js +export default compose( + constructWebexEnhancer({name: 'space', reducers}), + withIntl({locale: 'en', messages}) +)(ConnectedWidget); +``` + +## Incorrect + +```js +// Directly mount a widget and create an unrelated SDK/store lifecycle +export default ConnectedWidget; +``` + +**Why wrong:** direct export skips host registration, standard teardown, SDK state, version metadata, data attributes, and shared store composition. + +## Where it appears + +- `packages/node_modules/@webex/widget-space/src/index.js` +- `packages/node_modules/@webex/widget-recents/src/index.js` +- `packages/node_modules/@webex/widget-meetings/src/index.js` +- `packages/node_modules/@webex/widget-message/src/index.js` + +## Edge cases / exceptions + +- Newer TypeScript calling widgets expose adapter-context React components and do not use the legacy enhancer stack. +- Private demo entrypoints may mount already-enhanced widgets rather than define a new public widget. diff --git a/ai-docs/rules/README.md b/ai-docs/rules/README.md new file mode 100644 index 000000000..c472d3bbe --- /dev/null +++ b/ai-docs/rules/README.md @@ -0,0 +1,12 @@ +# Detailed repository rules + +Root `AGENTS.md` contains the critical subset and `../RULES.md` contains the catalog. Read these focused rules when changing public packages or asynchronous browser/SDK behavior. + +## Use Rules For + +- Reviewing public-export and runtime-lifecycle changes against explicit must/must-not constraints. +- Locating the tests or CI checks that enforce a rule. +- Keeping detailed rationale out of the compact root rule catalog. + +- `preserve-public-entrypoints.md` +- `clean-up-runtime-listeners.md` diff --git a/ai-docs/rules/clean-up-runtime-listeners.md b/ai-docs/rules/clean-up-runtime-listeners.md new file mode 100644 index 000000000..462aff058 --- /dev/null +++ b/ai-docs/rules/clean-up-runtime-listeners.md @@ -0,0 +1,28 @@ +<!-- ─────────────────────────────── + Template: Rule (example) + Template-ID: rule + Generates: ai-docs/rules/clean-up-runtime-listeners.md + Description: One enforceable repo rule — the rule, its rationale, how to follow it, and how it's enforced. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Rule: Clean up runtime listeners and mounted state + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md). + +## Rule + +Every browser, SDK, media, timer, observer, or realtime listener introduced by a component/HOC must have a matching teardown path. Browser widget removal must unmount React and clear the UUID registry entry; do not assume it dispatches `REMOVE_WIDGET` or explicitly clears Redux state. + +## Why + +Widgets can be mounted and removed repeatedly inside a host page. Leaked listeners duplicate events, retain credentials/resource state, and produce stale UI or media behavior. + +## How to follow + +Follow cleanup examples in `packages/node_modules/@webex/webex-widget-base/src/enhancers/withBrowserGlobals.js`, `packages/node_modules/@webex/widget-voice-mail/src/hooks/useAudio.ts`, and `packages/node_modules/@webex/widget-number-pad/src/SearchContacts/CallSelectPopover.tsx`. Add a negative test proving callbacks stop after unmount/removal. + +## Enforced by + +Jest lifecycle tests and review checks C4/C5/K1; review only for integration leaks not observable in unit tests. diff --git a/ai-docs/rules/preserve-public-entrypoints.md b/ai-docs/rules/preserve-public-entrypoints.md new file mode 100644 index 000000000..1c785bb4a --- /dev/null +++ b/ai-docs/rules/preserve-public-entrypoints.md @@ -0,0 +1,28 @@ +<!-- ─────────────────────────────── + Template: Rule (example) + Template-ID: rule + Generates: ai-docs/rules/preserve-public-entrypoints.md + Description: One enforceable repo rule — the rule, its rationale, how to follow it, and how it's enforced. + Library ver: 0.2.2 + Last updated: 2026-06-30 +─────────────────────────────── --> + +# Rule: Preserve public entrypoints + +> Navigation: [`AGENTS.md`](../../AGENTS.md) · [`SPEC_INDEX.md`](../SPEC_INDEX.md). + +## Rule + +Change a package `src/index.js|ts`, package `main`/`module`, widget name, data-toggle name, event string, or public prop/type only with an approved compatibility delta and synchronized contract documentation. + +## Why + +The repository publishes dozens of independently imported packages plus browser/CDN widgets. A seemingly local rename can break npm imports, host mounting, callbacks, or existing markup. + +## How to follow + +Use the exact source entrypoint and owning module spec; update `ai-docs/CONTRACTS.md`, `.sdd/manifest.json`, tests, changelog/migration guidance, and any generated artifact configuration in the same change. + +## Enforced by + +Review checks C1–C3, independent spec validation, Jest/journey tests where available, and release review. There is no single automated API-diff gate today.