diff --git a/.claude/skills/api-sync/SKILL.md b/.claude/skills/api-sync/SKILL.md
index 637ed7d72..02d21a3cf 100644
--- a/.claude/skills/api-sync/SKILL.md
+++ b/.claude/skills/api-sync/SKILL.md
@@ -1,371 +1,362 @@
----
-name: api-sync
-description: This skill should be used when regenerating Swagger/OpenAPI TypeScript API clients with breaking change detection. Handles generation of all 10 API clients (or specific ones), pre-generation impact analysis, Unicode cleanup, TypeScript validation, and affected test execution. Use when user requests "API sync", "regenerate swagger", "check breaking changes", or indicates backend API changes.
----
-
-# API Sync Manager
-
-## Overview
-
-Automate the complete lifecycle of TypeScript API client regeneration from Swagger/OpenAPI specifications. Provides pre-generation breaking change detection, automatic post-processing, impact analysis, validation, and migration recommendations for all 10 API clients in the ISA-Frontend monorepo.
-
-## When to Use This Skill
-
-Invoke when user requests:
-- API client regeneration or sync
-- "Check breaking changes" before API update
-- Backend API changes need frontend updates
-- Impact assessment of API changes
-- "Regenerate swagger" or "update API clients"
-
-## Available APIs
-
-availability-api, cat-search-api, checkout-api, crm-api, eis-api, inventory-api, isa-api, oms-api, print-api, wws-api
-
-## Unified Sync Workflow
-
-### Step 1: Pre-Generation Check
-
-```bash
-# Check uncommitted changes
-git status generated/swagger/
-
-# Verify no manual edits will be lost
-git diff generated/swagger/
-```
-
-If uncommitted changes exist, warn user and ask to proceed. If manual edits detected, strongly recommend committing first.
-
-### Step 2: Pre-Generation Breaking Change Detection
-
-Generate to temporary location to compare without affecting working directory:
-
-```bash
-# Backup current state
-cp -r generated/swagger/[api-name] /tmp/[api-name].backup
-
-# Generate to temp location for comparison
-npm run generate:swagger:[api-name]
-```
-
-**Compare Models and Services:**
-
-```bash
-diff -u /tmp/[api-name].backup/models.ts generated/swagger/[api-name]/models.ts
-diff -u /tmp/[api-name].backup/services.ts generated/swagger/[api-name]/services.ts
-```
-
-**Categorize Changes:**
-
-**π΄ Breaking (Critical):**
-- Removed properties from response models
-- Changed property types (string β number, object β array)
-- Removed endpoints
-- Optional β required fields in request models
-- Removed enum values
-- Changed endpoint paths or HTTP methods
-
-**β οΈ Warnings (Review Required):**
-- Property renamed (old removed + new added)
-- Changed default values
-- Changed validation rules (min/max length, pattern)
-- Added required request fields
-- Changed parameter locations (query β body)
-
-**β
Compatible (Safe):**
-- Added properties to response models
-- New endpoints
-- Added optional parameters
-- New enum values
-- Required β optional fields
-
-### Step 3: Impact Analysis
-
-For each breaking or warning change, analyze codebase impact:
-
-```bash
-# Find all imports from affected API
-grep -r "from '@generated/swagger/[api-name]" libs/ --include="*.ts"
-
-# Find usages of specific removed/changed items
-grep -r "[RemovedType|removedProperty|removedMethod]" libs/*/data-access --include="*.ts"
-```
-
-**Document:**
-- Affected files (with line numbers)
-- Services impacted
-- Components/stores using affected services
-- Estimated refactoring effort (hours)
-
-### Step 4: Generate Migration Strategy
-
-Based on breaking change severity:
-
-**High Impact (>5 breaking changes or critical endpoints):**
-1. Create dedicated migration branch from develop
-2. Document all changes in migration guide
-3. Update data-access services incrementally
-4. Update affected stores and components
-5. Comprehensive test coverage
-6. Coordinate deployment with backend team
-
-**Medium Impact (2-5 breaking changes):**
-1. Fix TypeScript compilation errors
-2. Update affected data-access services
-3. Update tests
-4. Run affected test suite
-5. Deploy with monitoring
-
-**Low Impact (1 breaking change, minor impact):**
-1. Apply minimal updates
-2. Verify tests pass
-3. Deploy normally
-
-### Step 5: Execute Generation
-
-If user approves proceeding after impact analysis:
-
-```bash
-# All APIs
-npm run generate:swagger
-
-# Specific API (if api-name provided)
-npm run generate:swagger:[api-name]
-```
-
-### Step 6: Verify Unicode Cleanup
-
-Automatic cleanup via `tools/fix-files.js` should execute during generation. Verify:
-
-```bash
-# Scan for remaining Unicode issues
-grep -r "\\\\u00" generated/swagger/ || echo "β
No Unicode issues"
-```
-
-If issues remain, run manually:
-
-```bash
-node tools/fix-files.js
-```
-
-### Step 7: TypeScript Validation
-
-```bash
-# Full TypeScript compilation check
-npx tsc --noEmit
-
-# If errors, show affected files
-npx tsc --noEmit | head -20
-```
-
-Document compilation errors:
-- Missing properties
-- Type mismatches
-- Incompatible signatures
-
-### Step 8: Run Affected Tests
-
-```bash
-# Run tests for affected projects
-npx nx affected:test --skip-nx-cache --base=HEAD~1
-
-# Lint affected projects
-npx nx affected:lint --base=HEAD~1
-```
-
-Monitor test failures and categorize:
-- Mock data mismatches (update fixtures)
-- Type assertion failures (update test types)
-- Integration test failures (API contract changes)
-
-### Step 9: Generate Comprehensive Report
-
-```
-API Sync Manager Report
-=======================
-
-API: [api-name | all]
-Sync Date: [timestamp]
-Generation: β
Success / β Failed
-
-π Change Summary
------------------
-Breaking Changes: XX
-Warnings: XX
-Compatible Changes: XX
-Files Modified: XX
-
-π΄ Breaking Changes
--------------------
-1. [API Name] - Removed Property: OrderResponse.deliveryDate
- Impact: Medium (2 files affected)
- Files:
- - libs/oms/data-access/src/lib/services/order.service.ts:45
- - libs/oms/feature/order-detail/src/lib/component.ts:78
- Fix Strategy: Remove references or use alternativeDate field
- Estimated Effort: 30 minutes
-
-2. [API Name] - Type Changed: ProductResponse.price (string β number)
- Impact: High (1 file + cascading changes)
- Files:
- - libs/catalogue/data-access/src/lib/services/product.service.ts:32
- Fix Strategy: Remove string parsing, update price calculations
- Estimated Effort: 1 hour
-
-β οΈ Warnings (Review Required)
-------------------------------
-1. [API Name] - Possible Rename: CustomerResponse.customerName β fullName
- Action: Verify with backend team if intentional
- Migration: Update references if confirmed rename
-
-2. [API Name] - New Required Field: CreateOrderRequest.taxId
- Action: Update order creation flows to provide taxId
- Impact: 3 order creation forms
-
-β
Compatible Changes
----------------------
-1. [API Name] - Added Property: OrderResponse.estimatedDelivery
-2. [API Name] - New Endpoint: GET /api/v2/orders/bulk
-3. [API Name] - Added Optional Parameter: includeArchived to GET /orders
-
-π Validation Results
----------------------
-TypeScript Compilation: β
Pass / β XX errors
-Unicode Cleanup: β
Complete
-Tests: XX/XX passing (YY affected)
-Lint: β
Pass / β οΈ XX warnings
-
-β Test Failures (if any)
--------------------------
-1. order.service.spec.ts - Mock response missing deliveryDate
- Fix: Update mock fixture
-2. product.component.spec.ts - Price type assertion failed
- Fix: Change expect(price).toBe("10.99") β expect(price).toBe(10.99)
-
-π‘ Migration Strategy
----------------------
-Approach: [High/Medium/Low Impact]
-Estimated Total Effort: [hours]
-
-Steps:
-1. [Fix compilation errors in data-access services]
-2. [Update test mocks and fixtures]
-3. [Update components using affected properties]
-4. [Run full test suite]
-5. [Deploy with backend coordination]
-
-π― Recommendation
------------------
-[One of the following:]
-β
Safe to proceed - Only compatible changes detected
-β οΈ Proceed with caution - Fix breaking changes before deployment
-π΄ Coordinate with backend - High impact changes require migration plan
-π Block regeneration - Critical breaking changes, backend rollback needed
-
-π Next Steps
--------------
-[Specific actions user should take]
-- [ ] Fix compilation errors in [files]
-- [ ] Update test mocks in [files]
-- [ ] Coordinate deployment timing with backend team
-- [ ] Monitor [specific endpoints] after deployment
-```
-
-### Step 10: Cleanup
-
-```bash
-# Remove temporary backup
-rm -rf /tmp/[api-name].backup
-
-# Or restore if generation needs to be reverted
-# cp -r /tmp/[api-name].backup generated/swagger/[api-name]
-```
-
-## Error Handling
-
-**Generation Fails:**
-- Check OpenAPI spec URLs in package.json scripts
-- Verify backend API is accessible
-- Check for malformed OpenAPI specification
-
-**Unicode Cleanup Fails:**
-- Run `node tools/fix-files.js` manually
-- Check for new Unicode patterns not covered by script
-
-**TypeScript Compilation Errors:**
-- Review breaking changes section in report
-- Update affected data-access services first
-- Fix cascading type errors in consumers
-
-**Test Failures:**
-- Update mock data to match new API contracts
-- Fix type assertions in tests
-- Update integration test expectations
-
-**Diff Detection Issues:**
-- Ensure clean git state before running
-- Check file permissions on generated/ directory
-- Verify backup location is writable
-
-## Advanced Usage
-
-**Compare Specific Endpoints Only:**
-
-```bash
-# Extract and compare specific interface
-grep -A 20 "interface OrderResponse" /tmp/[api-name].backup/models.ts
-grep -A 20 "interface OrderResponse" generated/swagger/[api-name]/models.ts
-```
-
-**Bulk API Sync with Change Detection:**
-
-Run pre-generation detection for all APIs before regenerating:
-
-```bash
-for api in availability-api cat-search-api checkout-api crm-api eis-api inventory-api isa-api oms-api print-api wws-api; do
- echo "Checking $api..."
- # Run detection workflow for each
-done
-```
-
-Generate consolidated report across all APIs before committing.
-
-**Rollback Procedure:**
-
-```bash
-# If sync causes critical issues
-git checkout generated/swagger/[api-name]
-git clean -fd generated/swagger/[api-name]
-
-# Or restore from backup
-cp -r generated/swagger.backup.[timestamp]/* generated/swagger/
-```
-
-## Integration with Git Workflow
-
-**Recommended Commit Message:**
-
-```
-feat(api): sync [api-name] API client [TASK-####]
-
-Breaking changes:
-- Removed OrderResponse.deliveryDate (use estimatedDelivery)
-- Changed ProductResponse.price type (string β number)
-
-Compatible changes:
-- Added OrderResponse.estimatedDelivery
-- New endpoint GET /api/v2/orders/bulk
-```
-
-**Branch Strategy:**
-
-- Low impact: Commit to feature branch directly
-- Medium impact: Create `sync/[api-name]-[date]` branch
-- High impact: Create `migration/[api-name]-[version]` branch with migration guide
-
-## References
-
-- CLAUDE.md API Integration section
-- package.json swagger generation scripts
-- tools/fix-files.js for Unicode cleanup logic
-- Semantic Versioning: https://semver.org
+---
+name: api-sync
+description: This skill should be used when regenerating Swagger/OpenAPI TypeScript API clients with breaking change detection. Handles generation of all 10 API clients (or specific ones), pre-generation impact analysis, Unicode cleanup, TypeScript validation, and affected test execution. Use when user requests "API sync", "regenerate swagger", "check breaking changes", or indicates backend API changes.
+---
+
+# API Sync Manager
+
+## Overview
+
+Automate the complete lifecycle of TypeScript API client regeneration from Swagger/OpenAPI specifications. Provides pre-generation breaking change detection, automatic post-processing, impact analysis, validation, and migration recommendations for all 10 API clients in the ISA-Frontend monorepo.
+
+## Available APIs
+
+availability-api, cat-search-api, checkout-api, crm-api, eis-api, inventory-api, isa-api, oms-api, print-api, wws-api
+
+## Unified Sync Workflow
+
+### Step 1: Pre-Generation Check
+
+```bash
+# Check uncommitted changes
+git status generated/swagger/
+
+# Verify no manual edits will be lost
+git diff generated/swagger/
+```
+
+If uncommitted changes exist, warn user and ask to proceed. If manual edits detected, strongly recommend committing first.
+
+### Step 2: Pre-Generation Breaking Change Detection
+
+Generate to temporary location to compare without affecting working directory:
+
+```bash
+# Backup current state
+cp -r generated/swagger/[api-name] /tmp/[api-name].backup
+
+# Generate to temp location for comparison
+npm run generate:swagger:[api-name]
+```
+
+**Compare Models and Services:**
+
+```bash
+diff -u /tmp/[api-name].backup/models.ts generated/swagger/[api-name]/models.ts
+diff -u /tmp/[api-name].backup/services.ts generated/swagger/[api-name]/services.ts
+```
+
+**Categorize Changes:**
+
+**π΄ Breaking (Critical):**
+- Removed properties from response models
+- Changed property types (string β number, object β array)
+- Removed endpoints
+- Optional β required fields in request models
+- Removed enum values
+- Changed endpoint paths or HTTP methods
+
+**β οΈ Warnings (Review Required):**
+- Property renamed (old removed + new added)
+- Changed default values
+- Changed validation rules (min/max length, pattern)
+- Added required request fields
+- Changed parameter locations (query β body)
+
+**β
Compatible (Safe):**
+- Added properties to response models
+- New endpoints
+- Added optional parameters
+- New enum values
+- Required β optional fields
+
+### Step 3: Impact Analysis
+
+For each breaking or warning change, analyze codebase impact:
+
+```bash
+# Find all imports from affected API
+grep -r "from '@generated/swagger/[api-name]" libs/ --include="*.ts"
+
+# Find usages of specific removed/changed items
+grep -r "[RemovedType|removedProperty|removedMethod]" libs/*/data-access --include="*.ts"
+```
+
+**Document:**
+- Affected files (with line numbers)
+- Services impacted
+- Components/stores using affected services
+- Estimated refactoring effort (hours)
+
+### Step 4: Generate Migration Strategy
+
+Based on breaking change severity:
+
+**High Impact (>5 breaking changes or critical endpoints):**
+1. Create dedicated migration branch from develop
+2. Document all changes in migration guide
+3. Update data-access services incrementally
+4. Update affected stores and components
+5. Comprehensive test coverage
+6. Coordinate deployment with backend team
+
+**Medium Impact (2-5 breaking changes):**
+1. Fix TypeScript compilation errors
+2. Update affected data-access services
+3. Update tests
+4. Run affected test suite
+5. Deploy with monitoring
+
+**Low Impact (1 breaking change, minor impact):**
+1. Apply minimal updates
+2. Verify tests pass
+3. Deploy normally
+
+### Step 5: Execute Generation
+
+If user approves proceeding after impact analysis:
+
+```bash
+# All APIs
+npm run generate:swagger
+
+# Specific API (if api-name provided)
+npm run generate:swagger:[api-name]
+```
+
+### Step 6: Verify Unicode Cleanup
+
+Automatic cleanup via `tools/fix-files.js` should execute during generation. Verify:
+
+```bash
+# Scan for remaining Unicode issues
+grep -r "\\\\u00" generated/swagger/ || echo "β
No Unicode issues"
+```
+
+If issues remain, run manually:
+
+```bash
+node tools/fix-files.js
+```
+
+### Step 7: TypeScript Validation
+
+```bash
+# Full TypeScript compilation check
+npx tsc --noEmit
+
+# If errors, show affected files
+npx tsc --noEmit | head -20
+```
+
+Document compilation errors:
+- Missing properties
+- Type mismatches
+- Incompatible signatures
+
+### Step 8: Run Affected Tests
+
+```bash
+# Run tests for affected projects
+npx nx affected:test --skip-nx-cache --base=HEAD~1
+
+# Lint affected projects
+npx nx affected:lint --base=HEAD~1
+```
+
+Monitor test failures and categorize:
+- Mock data mismatches (update fixtures)
+- Type assertion failures (update test types)
+- Integration test failures (API contract changes)
+
+### Step 9: Generate Comprehensive Report
+
+```
+API Sync Manager Report
+=======================
+
+API: [api-name | all]
+Sync Date: [timestamp]
+Generation: β
Success / β Failed
+
+π Change Summary
+-----------------
+Breaking Changes: XX
+Warnings: XX
+Compatible Changes: XX
+Files Modified: XX
+
+π΄ Breaking Changes
+-------------------
+1. [API Name] - Removed Property: OrderResponse.deliveryDate
+ Impact: Medium (2 files affected)
+ Files:
+ - libs/oms/data-access/src/lib/services/order.service.ts:45
+ - libs/oms/feature/order-detail/src/lib/component.ts:78
+ Fix Strategy: Remove references or use alternativeDate field
+ Estimated Effort: 30 minutes
+
+2. [API Name] - Type Changed: ProductResponse.price (string β number)
+ Impact: High (1 file + cascading changes)
+ Files:
+ - libs/catalogue/data-access/src/lib/services/product.service.ts:32
+ Fix Strategy: Remove string parsing, update price calculations
+ Estimated Effort: 1 hour
+
+β οΈ Warnings (Review Required)
+------------------------------
+1. [API Name] - Possible Rename: CustomerResponse.customerName β fullName
+ Action: Verify with backend team if intentional
+ Migration: Update references if confirmed rename
+
+2. [API Name] - New Required Field: CreateOrderRequest.taxId
+ Action: Update order creation flows to provide taxId
+ Impact: 3 order creation forms
+
+β
Compatible Changes
+---------------------
+1. [API Name] - Added Property: OrderResponse.estimatedDelivery
+2. [API Name] - New Endpoint: GET /api/v2/orders/bulk
+3. [API Name] - Added Optional Parameter: includeArchived to GET /orders
+
+π Validation Results
+---------------------
+TypeScript Compilation: β
Pass / β XX errors
+Unicode Cleanup: β
Complete
+Tests: XX/XX passing (YY affected)
+Lint: β
Pass / β οΈ XX warnings
+
+β Test Failures (if any)
+-------------------------
+1. order.service.spec.ts - Mock response missing deliveryDate
+ Fix: Update mock fixture
+2. product.component.spec.ts - Price type assertion failed
+ Fix: Change expect(price).toBe("10.99") β expect(price).toBe(10.99)
+
+π‘ Migration Strategy
+---------------------
+Approach: [High/Medium/Low Impact]
+Estimated Total Effort: [hours]
+
+Steps:
+1. [Fix compilation errors in data-access services]
+2. [Update test mocks and fixtures]
+3. [Update components using affected properties]
+4. [Run full test suite]
+5. [Deploy with backend coordination]
+
+π― Recommendation
+-----------------
+[One of the following:]
+β
Safe to proceed - Only compatible changes detected
+β οΈ Proceed with caution - Fix breaking changes before deployment
+π΄ Coordinate with backend - High impact changes require migration plan
+π Block regeneration - Critical breaking changes, backend rollback needed
+
+π Next Steps
+-------------
+[Specific actions user should take]
+- [ ] Fix compilation errors in [files]
+- [ ] Update test mocks in [files]
+- [ ] Coordinate deployment timing with backend team
+- [ ] Monitor [specific endpoints] after deployment
+```
+
+### Step 10: Cleanup
+
+```bash
+# Remove temporary backup
+rm -rf /tmp/[api-name].backup
+
+# Or restore if generation needs to be reverted
+# cp -r /tmp/[api-name].backup generated/swagger/[api-name]
+```
+
+## Error Handling
+
+**Generation Fails:**
+- Check OpenAPI spec URLs in package.json scripts
+- Verify backend API is accessible
+- Check for malformed OpenAPI specification
+
+**Unicode Cleanup Fails:**
+- Run `node tools/fix-files.js` manually
+- Check for new Unicode patterns not covered by script
+
+**TypeScript Compilation Errors:**
+- Review breaking changes section in report
+- Update affected data-access services first
+- Fix cascading type errors in consumers
+
+**Test Failures:**
+- Update mock data to match new API contracts
+- Fix type assertions in tests
+- Update integration test expectations
+
+**Diff Detection Issues:**
+- Ensure clean git state before running
+- Check file permissions on generated/ directory
+- Verify backup location is writable
+
+## Advanced Usage
+
+**Compare Specific Endpoints Only:**
+
+```bash
+# Extract and compare specific interface
+grep -A 20 "interface OrderResponse" /tmp/[api-name].backup/models.ts
+grep -A 20 "interface OrderResponse" generated/swagger/[api-name]/models.ts
+```
+
+**Bulk API Sync with Change Detection:**
+
+Run pre-generation detection for all APIs before regenerating:
+
+```bash
+for api in availability-api cat-search-api checkout-api crm-api eis-api inventory-api isa-api oms-api print-api wws-api; do
+ echo "Checking $api..."
+ # Run detection workflow for each
+done
+```
+
+Generate consolidated report across all APIs before committing.
+
+**Rollback Procedure:**
+
+```bash
+# If sync causes critical issues
+git checkout generated/swagger/[api-name]
+git clean -fd generated/swagger/[api-name]
+
+# Or restore from backup
+cp -r generated/swagger.backup.[timestamp]/* generated/swagger/
+```
+
+## Integration with Git Workflow
+
+**Recommended Commit Message:**
+
+```
+feat(api): sync [api-name] API client [TASK-####]
+
+Breaking changes:
+- Removed OrderResponse.deliveryDate (use estimatedDelivery)
+- Changed ProductResponse.price type (string β number)
+
+Compatible changes:
+- Added OrderResponse.estimatedDelivery
+- New endpoint GET /api/v2/orders/bulk
+```
+
+**Branch Strategy:**
+
+- Low impact: Commit to feature branch directly
+- Medium impact: Create `sync/[api-name]-[date]` branch
+- High impact: Create `migration/[api-name]-[version]` branch with migration guide
+
+## References
+
+- CLAUDE.md API Integration section
+- package.json swagger generation scripts
+- tools/fix-files.js for Unicode cleanup logic
+- Semantic Versioning: https://semver.org
diff --git a/.claude/skills/arch-docs/SKILL.md b/.claude/skills/arch-docs/SKILL.md
index e8aa546f6..28321ffe9 100644
--- a/.claude/skills/arch-docs/SKILL.md
+++ b/.claude/skills/arch-docs/SKILL.md
@@ -1,171 +1,177 @@
----
-name: arch-docs
-description: Generate architecture documentation (C4, Arc42, ADRs, PlantUML). Auto-invoke when user mentions "architecture docs", "C4 model", "ADR", "document architecture", "system design", or "create architecture diagram".
----
-
-# Architecture Documentation Skill
-
-Generate comprehensive architecture documentation using modern frameworks and best practices.
-
-## When to Use
-
-- Creating or updating architecture documentation
-- Generating C4 model diagrams (Context, Container, Component, Code)
-- Writing Architecture Decision Records (ADRs)
-- Documenting system design and component relationships
-- Creating PlantUML or Mermaid diagrams
-
-## Available Frameworks
-
-### C4 Model
-Best for: Visualizing software architecture at different abstraction levels
-
-Levels:
-1. **Context** - System landscape and external actors
-2. **Container** - High-level technology choices (apps, databases, etc.)
-3. **Component** - Internal structure of containers
-4. **Code** - Class/module level detail (optional)
-
-See: `@references/c4-model.md` for patterns and examples
-
-### Arc42 Template
-Best for: Comprehensive architecture documentation
-
-Sections:
-1. Introduction and Goals
-2. Constraints
-3. Context and Scope
-4. Solution Strategy
-5. Building Block View
-6. Runtime View
-7. Deployment View
-8. Cross-cutting Concepts
-9. Architecture Decisions
-10. Quality Requirements
-11. Risks and Technical Debt
-12. Glossary
-
-See: `@references/arc42.md` for template structure
-
-### Architecture Decision Records (ADRs)
-Best for: Documenting individual architectural decisions
-
-See: `@references/adr-template.md` for format and examples
-
-## Workflow
-
-### 1. Discovery Phase
-```bash
-# Find existing architecture files
-find . -name "*architecture*" -o -name "*.puml" -o -name "*.mmd"
-
-# Identify service boundaries
-cat nx.json docker-compose.yml
-
-# Check for existing ADRs
-ls -la docs/adr/ docs/decisions/
-```
-
-### 2. Analysis Phase
-- Analyze codebase structure (`libs/`, `apps/`)
-- Identify dependencies from `tsconfig.base.json` paths
-- Review service boundaries from `project.json` tags
-- Map data flow from API definitions
-
-### 3. Documentation Phase
-Based on the request, create appropriate documentation:
-
-**For C4 diagrams:**
-```
-docs/architecture/
-βββ c4-context.puml
-βββ c4-container.puml
-βββ c4-component-[name].puml
-```
-
-**For ADRs:**
-```
-docs/adr/
-βββ 0001-record-architecture-decisions.md
-βββ 0002-[decision-title].md
-βββ template.md
-```
-
-**For Arc42:**
-```
-docs/architecture/
-βββ arc42/
- βββ 01-introduction.md
- βββ 02-constraints.md
- βββ ...
-```
-
-## ISA-Frontend Specific Context
-
-### Monorepo Structure
-- **apps/**: Angular applications
-- **libs/**: Shared libraries organized by domain
- - `libs/[domain]/feature/` - Feature modules
- - `libs/[domain]/data-access/` - State management
- - `libs/[domain]/ui/` - Presentational components
- - `libs/[domain]/util/` - Utilities
-
-### Key Architectural Patterns
-- **Nx Monorepo** with strict module boundaries
-- **NgRx Signal Store** for state management
-- **Standalone Components** (Angular 20+)
-- **Domain-Driven Design** library organization
-
-### Documentation Locations
-- ADRs: `docs/adr/`
-- Architecture diagrams: `docs/architecture/`
-- API documentation: Generated from Swagger/OpenAPI
-
-## Output Standards
-
-### PlantUML Format
-```plantuml
-@startuml C4_Context
-!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
-
-Person(user, "User", "System user")
-System(system, "ISA System", "Main application")
-System_Ext(external, "External API", "Third-party service")
-
-Rel(user, system, "Uses")
-Rel(system, external, "Calls")
-@enduml
-```
-
-### Mermaid Format
-```mermaid
-graph TD
- A[User] --> B[ISA App]
- B --> C[API Gateway]
- C --> D[Backend Services]
-```
-
-### ADR Format
-```markdown
-# ADR-XXXX: [Title]
-
-## Status
-[Proposed | Accepted | Deprecated | Superseded]
-
-## Context
-[What is the issue?]
-
-## Decision
-[What was decided?]
-
-## Consequences
-[What are the results?]
-```
-
-## Best Practices
-
-1. **Start with Context** - Always begin with C4 Level 1 (System Context)
-2. **Use Consistent Notation** - Stick to one diagramming tool/format
-3. **Keep ADRs Atomic** - One decision per ADR
-4. **Version Control** - Commit documentation with code changes
-5. **Review Regularly** - Architecture docs decay; schedule reviews
+---
+name: arch-docs
+description: Generate architecture documentation (C4, Arc42, ADRs, PlantUML). Auto-invoke when user mentions "architecture docs", "C4 model", "ADR", "document architecture", "system design", or "create architecture diagram".
+---
+
+# Architecture Documentation
+
+Generate comprehensive architecture documentation using modern frameworks and best practices.
+
+## Available Frameworks
+
+### C4 Model
+Visualize software architecture at different abstraction levels.
+
+**Levels:**
+1. **Context** - System landscape and external actors
+2. **Container** - High-level technology choices (apps, databases, etc.)
+3. **Component** - Internal structure of containers
+4. **Code** - Class/module level detail (optional)
+
+**When to use:** Visualizing system architecture, creating diagrams for stakeholders at different technical levels.
+
+See `references/c4-model.md` for detailed patterns and examples.
+
+### Arc42 Template
+Comprehensive architecture documentation covering all aspects.
+
+**Sections:**
+1. Introduction and Goals
+2. Constraints
+3. Context and Scope
+4. Solution Strategy
+5. Building Block View
+6. Runtime View
+7. Deployment View
+8. Cross-cutting Concepts
+9. Architecture Decisions
+10. Quality Requirements
+11. Risks and Technical Debt
+12. Glossary
+
+**When to use:** Creating complete architecture documentation, documenting system-wide concerns, establishing quality goals.
+
+See `references/arc42.md` for complete template structure.
+
+### Architecture Decision Records (ADRs)
+Document individual architectural decisions with context and consequences.
+
+**When to use:** Recording significant decisions, documenting trade-offs, tracking architectural evolution.
+
+See `references/adr-template.md` for format and examples.
+
+## Workflow
+
+### 1. Discovery Phase
+Understand the system structure:
+
+```bash
+# Find existing architecture files
+find . -name "*architecture*" -o -name "*.puml" -o -name "*.mmd"
+
+# Identify service boundaries
+cat nx.json docker-compose.yml
+
+# Check for existing ADRs
+ls -la docs/adr/ docs/decisions/
+```
+
+### 2. Analysis Phase
+- Analyze codebase structure (`libs/`, `apps/`)
+- Identify dependencies from `tsconfig.base.json` paths
+- Review service boundaries from `project.json` tags
+- Map data flow from API definitions
+
+### 3. Documentation Phase
+Choose appropriate output format based on request:
+
+**For C4 diagrams:**
+```
+docs/architecture/
+βββ c4-context.puml
+βββ c4-container.puml
+βββ c4-component-[name].puml
+```
+
+**For ADRs:**
+```
+docs/adr/
+βββ 0001-record-architecture-decisions.md
+βββ 0002-[decision-title].md
+βββ template.md
+```
+
+**For Arc42:**
+```
+docs/architecture/
+βββ arc42/
+ βββ 01-introduction.md
+ βββ 02-constraints.md
+ βββ ...
+```
+
+## ISA-Frontend Context
+
+### Monorepo Structure
+- **apps/**: Angular applications
+- **libs/**: Shared libraries organized by domain
+ - `libs/[domain]/feature/` - Feature modules
+ - `libs/[domain]/data-access/` - State management
+ - `libs/[domain]/ui/` - Presentational components
+ - `libs/[domain]/util/` - Utilities
+
+### Key Architectural Patterns
+- **Nx Monorepo** with strict module boundaries
+- **NgRx Signal Store** for state management
+- **Standalone Components** (Angular 20+)
+- **Domain-Driven Design** library organization
+
+### Documentation Locations
+- ADRs: `docs/adr/`
+- Architecture diagrams: `docs/architecture/`
+- API documentation: Generated from Swagger/OpenAPI
+
+## Output Standards
+
+### PlantUML Format
+```plantuml
+@startuml C4_Context
+!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
+
+Person(user, "User", "System user")
+System(system, "ISA System", "Main application")
+System_Ext(external, "External API", "Third-party service")
+
+Rel(user, system, "Uses")
+Rel(system, external, "Calls")
+@enduml
+```
+
+### Mermaid Format
+```mermaid
+graph TD
+ A[User] --> B[ISA App]
+ B --> C[API Gateway]
+ C --> D[Backend Services]
+```
+
+### ADR Format
+```markdown
+# ADR-XXXX: [Title]
+
+## Status
+[Proposed | Accepted | Deprecated | Superseded]
+
+## Context
+[What is the issue?]
+
+## Decision
+[What was decided?]
+
+## Consequences
+[What are the results?]
+```
+
+## Best Practices
+
+1. **Start with Context** - Always begin with C4 Level 1 (System Context)
+2. **Use Consistent Notation** - Stick to one diagramming tool/format
+3. **Keep ADRs Atomic** - One decision per ADR
+4. **Version Control** - Commit documentation with code changes
+5. **Review Regularly** - Architecture docs decay; schedule reviews
+
+## References
+
+- `references/c4-model.md` - C4 model patterns, templates, and ISA-Frontend domain structure
+- `references/arc42.md` - Complete Arc42 template with all 12 sections
+- `references/adr-template.md` - ADR format with examples and naming conventions
diff --git a/.claude/skills/architecture-validator/SKILL.md b/.claude/skills/architecture-validator/SKILL.md
index b2f2406a9..e66e8bcb7 100644
--- a/.claude/skills/architecture-validator/SKILL.md
+++ b/.claude/skills/architecture-validator/SKILL.md
@@ -9,16 +9,6 @@ description: This skill should be used when validating architecture compliance,
Validate and enforce architectural boundaries in the ISA-Frontend monorepo. Detects import boundary violations, circular dependencies, layer violations, and cross-domain dependencies. Provides automated fix strategies using graph analysis, dependency injection, interface extraction, and shared code refactoring.
-## When to Use This Skill
-
-Invoke when user wants to:
-- Validate import boundaries or architectural rules
-- Detect circular dependencies or dependency cycles
-- Find layer violations (FeatureβFeature, Data AccessβFeature)
-- Check cross-domain dependencies
-- Resolve build failures with circular import warnings
-- Mentioned "check architecture", "validate boundaries", "circular dependencies", or "dependency cycles"
-
## Architectural Rules
**Allowed Dependencies:**
diff --git a/.claude/skills/css-animations/SKILL.md b/.claude/skills/css-animations/SKILL.md
index ffa46fa3b..a7ba7ed7d 100644
--- a/.claude/skills/css-animations/SKILL.md
+++ b/.claude/skills/css-animations/SKILL.md
@@ -9,17 +9,6 @@ description: This skill should be used when writing or reviewing CSS animations
Implement native CSS @keyframes animations for Angular applications, replacing @angular/animations with GPU-accelerated, zero-bundle-size alternatives. This skill provides comprehensive guidance on creating performant entrance/exit animations, staggered effects, and proper timing configurations.
-## When to Use This Skill
-
-Apply this skill when:
-- **Writing Angular components** with entrance/exit animations
-- **Converting @angular/animations** to native CSS @keyframes
-- **Implementing animate.enter/animate.leave** in Angular 20+ templates
-- **Creating staggered animations** for lists or collections
-- **Debugging animation issues** (snap-back, wrong starting positions, choppy playback)
-- **Optimizing animation performance** for GPU acceleration
-- **Reviewing animation code** for accessibility and best practices
-
## Quick Start
### Basic Animation Setup
@@ -311,41 +300,6 @@ element.addEventListener('animationend', (e) => {
});
```
-## Resources
-
-### references/keyframes-guide.md
-
-Comprehensive deep-dive covering:
-- Complete @keyframes syntax reference
-- Detailed timing functions and cubic-bezier curves
-- Advanced techniques (direction, play-state, @starting-style)
-- Performance optimization strategies
-- Extensive common patterns library
-- Debugging techniques and troubleshooting
-
-**When to reference:** Complex animation requirements, custom easing curves, advanced techniques, performance optimization, or learning comprehensive details.
-
-### assets/animations.css
-
-Ready-to-use CSS file with common animation patterns:
-- Fade animations (in/out)
-- Slide animations (up/down/left/right)
-- Scale animations (in/out)
-- Utility animations (spin, shimmer, shake, breathe, attention-pulse)
-- Toast/notification animations
-- Accessibility (@media prefers-reduced-motion)
-
-**Usage:** Copy this file to project and import in component styles or global styles:
-
-```css
-@import 'path/to/animations.css';
-```
-
-Then use classes directly:
-```html
-
-```
-
## Migration from @angular/animations
### Before (Angular Animations)
@@ -390,3 +344,38 @@ import { trigger, state, style, transition, animate } from '@angular/animations'
- GPU hardware acceleration
- Standard CSS (transferable skills)
- Better performance
+
+## Resources
+
+### references/keyframes-guide.md
+
+Comprehensive deep-dive covering:
+- Complete @keyframes syntax reference
+- Detailed timing functions and cubic-bezier curves
+- Advanced techniques (direction, play-state, @starting-style)
+- Performance optimization strategies
+- Extensive common patterns library
+- Debugging techniques and troubleshooting
+
+**When to reference:** Complex animation requirements, custom easing curves, advanced techniques, performance optimization, or learning comprehensive details.
+
+### assets/animations.css
+
+Ready-to-use CSS file with common animation patterns:
+- Fade animations (in/out)
+- Slide animations (up/down/left/right)
+- Scale animations (in/out)
+- Utility animations (spin, shimmer, shake, breathe, attention-pulse)
+- Toast/notification animations
+- Accessibility (@media prefers-reduced-motion)
+
+**Usage:** Copy this file to project and import in component styles or global styles:
+
+```css
+@import 'path/to/animations.css';
+```
+
+Then use classes directly:
+```html
+
+```
diff --git a/.claude/skills/git-workflow/SKILL.md b/.claude/skills/git-workflow/SKILL.md
index af1c2b985..7fd007e99 100644
--- a/.claude/skills/git-workflow/SKILL.md
+++ b/.claude/skills/git-workflow/SKILL.md
@@ -1,352 +1,352 @@
----
-name: git-workflow
-description: This skill should be used when creating branches, writing commits, or creating pull requests. Enforces ISA-Frontend Git conventions including feature/task-id-name branch format, conventional commits without co-author tags, and PRs targeting develop branch.
----
-
-# Git Workflow Skill
-
-Enforces Git workflow conventions specific to the ISA-Frontend project.
-
-## When to Use
-
-- Creating new branches for features or bugfixes
-- Writing commit messages
-- Creating pull requests
-- Any Git operations requiring adherence to project conventions
-
-## Core Principles
-
-### 1. Default Branch is `develop` (NOT `main`)
-
-- **All PRs target**: `develop` branch
-- **Feature branches from**: `develop`
-- **Never push directly to**: `develop` or `main`
-
-### 2. Branch Naming Convention
-
-**Format**: `
/{task-id}-{short-description}`
-
-**Types**:
-- `feature/` - New features or enhancements
-- `bugfix/` - Bug fixes
-- `hotfix/` - Emergency production fixes
-
-**Rules**:
-- Use English kebab-case for descriptions
-- Start with task/issue ID (e.g., `5391`)
-- Keep description concise - shorten if too long
-- Use hyphens to separate words
-
-**Examples**:
-```bash
-# Good
-feature/5391-praemie-checkout-action-card-delivery-order
-bugfix/6123-fix-login-redirect-loop
-hotfix/7890-critical-payment-error
-
-# Bad
-feature/praemie-checkout # Missing task ID
-feature/5391_praemie # Using underscores
-feature-5391-very-long-description-that-goes-on-forever # Too long
-```
-
-### 3. Conventional Commits (WITHOUT Co-Author Tags)
-
-**Format**: `(): `
-
-**Types**:
-- `feat`: New feature
-- `fix`: Bug fix
-- `docs`: Documentation only
-- `style`: Code style (formatting, missing semicolons)
-- `refactor`: Code restructuring without feature changes
-- `perf`: Performance improvements
-- `test`: Adding or updating tests
-- `build`: Build system or dependencies
-- `ci`: CI configuration
-- `chore`: Maintenance tasks
-
-**Rules**:
-- β **NO** "Generated with Claude Code" tags
-- β **NO** "Co-Authored-By: Claude" tags
-- β
Keep first line under 72 characters
-- β
Use imperative mood ("add" not "added")
-- β
Body optional but recommended for complex changes
-
-**Examples**:
-```bash
-# Good
-feat(checkout): add bonus card selection for delivery orders
-
-fix(crm): resolve customer search filter reset issue
-
-refactor(oms): extract return validation logic into service
-
-# Bad
-feat(checkout): add bonus card selection
-
-Generated with Claude Code
-Co-Authored-By: Claude
-
-# Also bad
-Added new feature # Wrong tense
-Fix bug # Missing scope
-```
-
-### 4. Pull Request Creation
-
-**Target Branch**: Always `develop`
-
-**PR Title Format**: Same as conventional commit
-```
-feat(domain): concise description of changes
-```
-
-**PR Body Structure**:
-```markdown
-## Summary
-- Brief bullet points of changes
-
-## Related Tasks
-- Closes #{task-id}
-- Refs #{related-task}
-
-## Test Plan
-- [ ] Unit tests added/updated
-- [ ] E2E attributes added
-- [ ] Manual testing completed
-
-## Breaking Changes
-None / List breaking changes
-
-## Screenshots (if UI changes)
-[Add screenshots]
-```
-
-## Common Workflows
-
-### Creating a Feature Branch
-
-```bash
-# 1. Update develop
-git checkout develop
-git pull origin develop
-
-# 2. Create feature branch
-git checkout -b feature/5391-praemie-checkout-action-card
-
-# 3. Work and commit
-git add .
-git commit -m "feat(checkout): add primary bonus card selection logic"
-
-# 4. Push to remote
-git push -u origin feature/5391-praemie-checkout-action-card
-
-# 5. Create PR targeting develop (use gh CLI or web UI)
-```
-
-### Creating a Bugfix Branch
-
-```bash
-# From develop
-git checkout develop
-git pull origin develop
-git checkout -b bugfix/6123-login-redirect-loop
-
-# Commit
-git commit -m "fix(auth): resolve infinite redirect on logout"
-```
-
-### Creating a Hotfix Branch
-
-```bash
-# From main (production)
-git checkout main
-git pull origin main
-git checkout -b hotfix/7890-payment-processing-error
-
-# Commit
-git commit -m "fix(checkout): critical payment API timeout handling"
-
-# Merge to both main and develop
-```
-
-## Commit Message Guidelines
-
-### Good Commit Messages
-
-```bash
-feat(crm): add customer loyalty tier calculation
-
-Implements three-tier loyalty system based on annual spend.
-Includes migration for existing customer data.
-
-Refs #5234
-
----
-
-fix(oms): prevent duplicate return submissions
-
-Adds debouncing to return form submission and validates
-against existing returns in the last 60 seconds.
-
-Closes #5891
-
----
-
-refactor(catalogue): extract product search into dedicated service
-
-Moves search logic from component to ProductSearchService
-for better testability and reusability.
-
----
-
-perf(remission): optimize remission list query with pagination
-
-Reduces initial load time from 3s to 800ms by implementing
-cursor-based pagination.
-
-Closes #6234
-```
-
-### Bad Commit Messages
-
-```bash
-# Too vague
-fix: bug fixes
-
-# Missing scope
-feat: new feature
-
-# Wrong tense
-fixed the login issue
-
-# Including banned tags
-feat(checkout): add feature
-
-Generated with Claude Code
-Co-Authored-By: Claude
-```
-
-## Git Configuration Checks
-
-### Verify Git Setup
-
-```bash
-# Check current branch
-git branch --show-current
-
-# Verify remote
-git remote -v # Should show origin pointing to ISA-Frontend
-
-# Check for uncommitted changes
-git status
-```
-
-## Common Mistakes to Avoid
-
-```bash
-# β Creating PR against main
-gh pr create --base main # WRONG
-
-# β
Always target develop
-gh pr create --base develop # CORRECT
-
-# β Using underscores in branch names
-git checkout -b feature/5391_my_feature # WRONG
-
-# β
Use hyphens
-git checkout -b feature/5391-my-feature # CORRECT
-
-# β Adding co-author tags
-git commit -m "feat: something
-
-Co-Authored-By: Claude " # WRONG
-
-# β
Clean commit message
-git commit -m "feat(scope): something" # CORRECT
-
-# β Forgetting task ID in branch name
-git checkout -b feature/new-checkout-flow # WRONG
-
-# β
Include task ID
-git checkout -b feature/5391-new-checkout-flow # CORRECT
-```
-
-## Integration with Claude Code
-
-When Claude Code creates commits or PRs:
-
-### Commit Creation
-```bash
-# Claude uses conventional commits WITHOUT attribution
-git commit -m "feat(checkout): implement bonus card selection
-
-Adds logic for selecting primary bonus card during checkout
-for delivery orders. Includes validation and error handling.
-
-Refs #5391"
-```
-
-### PR Creation
-```bash
-# Target develop by default
-gh pr create --base develop \
- --title "feat(checkout): implement bonus card selection" \
- --body "## Summary
-- Add primary bonus card selection logic
-- Implement validation for delivery orders
-- Add error handling for API failures
-
-## Related Tasks
-- Closes #5391
-
-## Test Plan
-- [x] Unit tests added
-- [x] E2E attributes added
-- [x] Manual testing completed"
-```
-
-## Branch Cleanup
-
-### After PR Merge
-```bash
-# Update develop
-git checkout develop
-git pull origin develop
-
-# Delete local feature branch
-git branch -d feature/5391-praemie-checkout
-
-# Delete remote branch (usually done by PR merge)
-git push origin --delete feature/5391-praemie-checkout
-```
-
-## Quick Reference
-
-```bash
-# Branch naming
-feature/{task-id}-{description}
-bugfix/{task-id}-{description}
-hotfix/{task-id}-{description}
-
-# Commit format
-():
-
-# Common types
-feat, fix, docs, style, refactor, perf, test, build, ci, chore
-
-# PR target
-Always: develop (NOT main)
-
-# Banned in commits
-- "Generated with Claude Code"
-- "Co-Authored-By: Claude"
-- Any AI attribution
-```
-
-## Resources
-
-- [Conventional Commits](https://www.conventionalcommits.org/)
-- Project PR template: `.github/pull_request_template.md`
-- Code review standards: `.github/review-instructions.md`
+---
+name: git-workflow
+description: This skill should be used when creating branches, writing commits, or creating pull requests. Enforces ISA-Frontend Git conventions including feature/task-id-name branch format, conventional commits without co-author tags, and PRs targeting develop branch.
+---
+
+# Git Workflow Skill
+
+Enforces Git workflow conventions specific to the ISA-Frontend project.
+
+## When to Use
+
+- Creating new branches for features or bugfixes
+- Writing commit messages
+- Creating pull requests
+- Any Git operations requiring adherence to project conventions
+
+## Core Principles
+
+### 1. Default Branch is `develop` (NOT `main`)
+
+- **All PRs target**: `develop` branch
+- **Feature branches from**: `develop`
+- **Never push directly to**: `develop` or `main`
+
+### 2. Branch Naming Convention
+
+**Format**: `/{task-id}-{short-description}`
+
+**Types**:
+- `feature/` - New features or enhancements
+- `bugfix/` - Bug fixes
+- `hotfix/` - Emergency production fixes
+
+**Rules**:
+- Use English kebab-case for descriptions
+- Start with task/issue ID (e.g., `5391`)
+- Keep description concise - shorten if too long
+- Use hyphens to separate words
+
+**Examples**:
+```bash
+# Good
+feature/5391-praemie-checkout-action-card-delivery-order
+bugfix/6123-fix-login-redirect-loop
+hotfix/7890-critical-payment-error
+
+# Bad
+feature/praemie-checkout # Missing task ID
+feature/5391_praemie # Using underscores
+feature-5391-very-long-description-that-goes-on-forever # Too long
+```
+
+### 3. Conventional Commits (WITHOUT Co-Author Tags)
+
+**Format**: `(): `
+
+**Types**:
+- `feat`: New feature
+- `fix`: Bug fix
+- `docs`: Documentation only
+- `style`: Code style (formatting, missing semicolons)
+- `refactor`: Code restructuring without feature changes
+- `perf`: Performance improvements
+- `test`: Adding or updating tests
+- `build`: Build system or dependencies
+- `ci`: CI configuration
+- `chore`: Maintenance tasks
+
+**Rules**:
+- β **NO** "Generated with Claude Code" tags
+- β **NO** "Co-Authored-By: Claude" tags
+- β
Keep first line under 72 characters
+- β
Use imperative mood ("add" not "added")
+- β
Body optional but recommended for complex changes
+
+**Examples**:
+```bash
+# Good
+feat(checkout): add bonus card selection for delivery orders
+
+fix(crm): resolve customer search filter reset issue
+
+refactor(oms): extract return validation logic into service
+
+# Bad
+feat(checkout): add bonus card selection
+
+Generated with Claude Code
+Co-Authored-By: Claude
+
+# Also bad
+Added new feature # Wrong tense
+Fix bug # Missing scope
+```
+
+### 4. Pull Request Creation
+
+**Target Branch**: Always `develop`
+
+**PR Title Format**: Same as conventional commit
+```
+feat(domain): concise description of changes
+```
+
+**PR Body Structure**:
+```markdown
+## Summary
+- Brief bullet points of changes
+
+## Related Tasks
+- Closes #{task-id}
+- Refs #{related-task}
+
+## Test Plan
+- [ ] Unit tests added/updated
+- [ ] E2E attributes added
+- [ ] Manual testing completed
+
+## Breaking Changes
+None / List breaking changes
+
+## Screenshots (if UI changes)
+[Add screenshots]
+```
+
+## Common Workflows
+
+### Creating a Feature Branch
+
+```bash
+# 1. Update develop
+git checkout develop
+git pull origin develop
+
+# 2. Create feature branch
+git checkout -b feature/5391-praemie-checkout-action-card
+
+# 3. Work and commit
+git add .
+git commit -m "feat(checkout): add primary bonus card selection logic"
+
+# 4. Push to remote
+git push -u origin feature/5391-praemie-checkout-action-card
+
+# 5. Create PR targeting develop (use gh CLI or web UI)
+```
+
+### Creating a Bugfix Branch
+
+```bash
+# From develop
+git checkout develop
+git pull origin develop
+git checkout -b bugfix/6123-login-redirect-loop
+
+# Commit
+git commit -m "fix(auth): resolve infinite redirect on logout"
+```
+
+### Creating a Hotfix Branch
+
+```bash
+# From main (production)
+git checkout main
+git pull origin main
+git checkout -b hotfix/7890-payment-processing-error
+
+# Commit
+git commit -m "fix(checkout): critical payment API timeout handling"
+
+# Merge to both main and develop
+```
+
+## Commit Message Guidelines
+
+### Good Commit Messages
+
+```bash
+feat(crm): add customer loyalty tier calculation
+
+Implements three-tier loyalty system based on annual spend.
+Includes migration for existing customer data.
+
+Refs #5234
+
+---
+
+fix(oms): prevent duplicate return submissions
+
+Adds debouncing to return form submission and validates
+against existing returns in the last 60 seconds.
+
+Closes #5891
+
+---
+
+refactor(catalogue): extract product search into dedicated service
+
+Moves search logic from component to ProductSearchService
+for better testability and reusability.
+
+---
+
+perf(remission): optimize remission list query with pagination
+
+Reduces initial load time from 3s to 800ms by implementing
+cursor-based pagination.
+
+Closes #6234
+```
+
+### Bad Commit Messages
+
+```bash
+# Too vague
+fix: bug fixes
+
+# Missing scope
+feat: new feature
+
+# Wrong tense
+fixed the login issue
+
+# Including banned tags
+feat(checkout): add feature
+
+Generated with Claude Code
+Co-Authored-By: Claude
+```
+
+## Git Configuration Checks
+
+### Verify Git Setup
+
+```bash
+# Check current branch
+git branch --show-current
+
+# Verify remote
+git remote -v # Should show origin pointing to ISA-Frontend
+
+# Check for uncommitted changes
+git status
+```
+
+## Common Mistakes to Avoid
+
+```bash
+# β Creating PR against main
+gh pr create --base main # WRONG
+
+# β
Always target develop
+gh pr create --base develop # CORRECT
+
+# β Using underscores in branch names
+git checkout -b feature/5391_my_feature # WRONG
+
+# β
Use hyphens
+git checkout -b feature/5391-my-feature # CORRECT
+
+# β Adding co-author tags
+git commit -m "feat: something
+
+Co-Authored-By: Claude " # WRONG
+
+# β
Clean commit message
+git commit -m "feat(scope): something" # CORRECT
+
+# β Forgetting task ID in branch name
+git checkout -b feature/new-checkout-flow # WRONG
+
+# β
Include task ID
+git checkout -b feature/5391-new-checkout-flow # CORRECT
+```
+
+## Integration with Claude Code
+
+When Claude Code creates commits or PRs:
+
+### Commit Creation
+```bash
+# Claude uses conventional commits WITHOUT attribution
+git commit -m "feat(checkout): implement bonus card selection
+
+Adds logic for selecting primary bonus card during checkout
+for delivery orders. Includes validation and error handling.
+
+Refs #5391"
+```
+
+### PR Creation
+```bash
+# Target develop by default
+gh pr create --base develop \
+ --title "feat(checkout): implement bonus card selection" \
+ --body "## Summary
+- Add primary bonus card selection logic
+- Implement validation for delivery orders
+- Add error handling for API failures
+
+## Related Tasks
+- Closes #5391
+
+## Test Plan
+- [x] Unit tests added
+- [x] E2E attributes added
+- [x] Manual testing completed"
+```
+
+## Branch Cleanup
+
+### After PR Merge
+```bash
+# Update develop
+git checkout develop
+git pull origin develop
+
+# Delete local feature branch
+git branch -d feature/5391-praemie-checkout
+
+# Delete remote branch (usually done by PR merge)
+git push origin --delete feature/5391-praemie-checkout
+```
+
+## Quick Reference
+
+```bash
+# Branch naming
+feature/{task-id}-{description}
+bugfix/{task-id}-{description}
+hotfix/{task-id}-{description}
+
+# Commit format
+():
+
+# Common types
+feat, fix, docs, style, refactor, perf, test, build, ci, chore
+
+# PR target
+Always: develop (NOT main)
+
+# Banned in commits
+- "Generated with Claude Code"
+- "Co-Authored-By: Claude"
+- Any AI attribution
+```
+
+## Resources
+
+- [Conventional Commits](https://www.conventionalcommits.org/)
+- Project PR template: `.github/pull_request_template.md`
+- Code review standards: `.github/review-instructions.md`
diff --git a/.claude/skills/library-creator/SKILL.md b/.claude/skills/library-creator/SKILL.md
index 3506d9320..c637730a6 100644
--- a/.claude/skills/library-creator/SKILL.md
+++ b/.claude/skills/library-creator/SKILL.md
@@ -1,284 +1,277 @@
----
-name: library-creator
-description: This skill should be used when creating feature/data-access/ui/util libraries or user says "create library", "new library", "scaffold library". Creates new Angular libraries in ISA-Frontend monorepo with proper Nx configuration, Vitest setup, architectural tags, and path aliases.
----
-
-# Library Creator
-
-## Overview
-
-Automate the creation of new Angular libraries following ISA-Frontend conventions. This skill handles the complete scaffolding workflow including Nx generation, Vitest configuration with CI/CD integration, architectural tags, path alias verification, and initial validation.
-
-## When to Use This Skill
-
-Invoke this skill when:
-- User requests creating a new library
-- User mentions "new library", "scaffold library", or "create feature"
-- User wants to add a new domain/layer/feature to the monorepo
-
-## Required Parameters
-
-Collect these parameters from the user:
-- **domain**: Domain name (oms, remission, checkout, ui, core, shared, utils)
-- **layer**: Layer type (feature, data-access, ui, util)
-- **name**: Library name in kebab-case
-
-## Library Creation Workflow
-
-### Step 1: Validate Input
-
-**Verify Domain:**
-- Use `docs-researcher` to check `docs/library-reference.md`
-- Ensure domain follows existing patterns
-
-**Validate Layer:**
-- Must be one of: feature, data-access, ui, util
-
-**Check Name:**
-- Must be kebab-case
-- Must not conflict with existing libraries
-
-**Determine Path Depth:**
-- 3 levels: `libs/domain/layer/name` β `../../../`
-- 4 levels: `libs/domain/type/layer/name` β `../../../../`
-
-### Step 2: Run Dry-Run
-
-Execute Nx generator with `--dry-run` to preview changes:
-
-```bash
-npx nx generate @nx/angular:library \
- --name=[domain]-[layer]-[name] \
- --directory=libs/[domain]/[layer]/[name] \
- --importPath=@isa/[domain]/[layer]/[name] \
- --prefix=[domain] \
- --style=css \
- --unitTestRunner=vitest \
- --standalone=true \
- --skipTests=false \
- --dry-run
-```
-
-Review output with user before proceeding.
-
-### Step 3: Generate Library
-
-Execute without `--dry-run`:
-
-```bash
-npx nx generate @nx/angular:library \
- --name=[domain]-[layer]-[name] \
- --directory=libs/[domain]/[layer]/[name] \
- --importPath=@isa/[domain]/[layer]/[name] \
- --prefix=[domain] \
- --style=css \
- --unitTestRunner=vitest \
- --standalone=true \
- --skipTests=false
-```
-
-### Step 4: Add Architectural Tags
-
-**CRITICAL**: Immediately after library generation, add proper tags to `project.json` for `@nx/enforce-module-boundaries`.
-
-Run the tagging script:
-```bash
-node scripts/add-library-tags.js
-```
-
-Or manually add tags to `libs/[domain]/[layer]/[name]/project.json`:
-
-```json
-{
- "name": "[domain]-[layer]-[name]",
- "tags": [
- "scope:[domain]",
- "type:[layer]"
- ]
-}
-```
-
-**Tag Rules:**
-- **Scope tag**: `scope:[domain]` (e.g., `scope:oms`, `scope:crm`, `scope:ui`, `scope:shared`)
-- **Type tag**: `type:[layer]` (e.g., `type:feature`, `type:data-access`, `type:ui`, `type:util`)
-
-**Examples:**
-- `libs/oms/feature/return-search` β `["scope:oms", "type:feature"]`
-- `libs/ui/buttons` β `["scope:ui", "type:ui"]`
-- `libs/shared/scanner` β `["scope:shared", "type:shared"]`
-- `libs/core/auth` β `["scope:core", "type:core"]`
-
-**Verification:**
-```bash
-# Check tags were added
-cat libs/[domain]/[layer]/[name]/project.json | jq '.tags'
-
-# Should output: ["scope:[domain]", "type:[layer]"]
-```
-
-### Step 5: Configure Vitest with JUnit and Cobertura
-
-Update `libs/[path]/vite.config.mts` with this template:
-
-```typescript
-///
-import { defineConfig } from 'vite';
-import angular from '@analogjs/vite-plugin-angular';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
-
-export default
-// @ts-expect-error - Vitest reporter tuple types have complex inference issues
-defineConfig(() => ({
- root: __dirname,
- cacheDir: '../../../node_modules/.vite/libs/[path]',
- plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
- test: {
- watch: false,
- globals: true,
- environment: 'jsdom',
- include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- setupFiles: ['src/test-setup.ts'],
- reporters: [
- 'default',
- ['junit', { outputFile: '../../../testresults/junit-[library-name].xml' }],
- ],
- coverage: {
- reportsDirectory: '../../../coverage/libs/[path]',
- provider: 'v8' as const,
- reporter: ['text', 'cobertura'],
- },
- },
-}));
-```
-
-**Critical**: Adjust path depth (`../../../` or `../../../../`) based on library location.
-
-### Step 6: Verify Configuration
-
-**Check Path Alias:**
-- Verify `tsconfig.base.json` was updated
-- Should have: `"@isa/[domain]/[layer]/[name]": ["libs/[domain]/[layer]/[name]/src/index.ts"]`
-
-**Run Initial Test:**
-```bash
-npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
-```
-
-**Verify CI/CD Files Created:**
-- JUnit XML: `testresults/junit-[library-name].xml`
-- Cobertura XML: `coverage/libs/[path]/cobertura-coverage.xml`
-
-### Step 7: Create Library README
-
-Use `docs-researcher` to find similar library READMEs, then create comprehensive documentation including:
-- Overview and purpose
-- Installation/import instructions
-- API documentation
-- Usage examples
-- Testing information (Vitest + Angular Testing Utilities)
-
-### Step 8: Update Library Reference
-
-Add entry to `docs/library-reference.md` under appropriate domain:
-
-```markdown
-#### `@isa/[domain]/[layer]/[name]`
-**Path:** `libs/[domain]/[layer]/[name]`
-**Type:** [Feature/Data Access/UI/Util]
-**Testing:** Vitest
-
-[Brief description]
-```
-
-### Step 9: Run Full Validation
-
-Execute validation commands to ensure library is properly configured:
-
-```bash
-# Lint (includes boundary checks)
-npx nx lint [library-name]
-
-# Test with coverage
-npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
-
-# Build (if buildable)
-npx nx build [library-name]
-
-# Dependency graph
-npx nx graph --focus=[library-name]
-```
-
-### Step 10: Generate Creation Report
-
-Provide this structured report to the user:
-
-```
-Library Created Successfully
-============================
-
-Library Name: [domain]-[layer]-[name]
-Path: libs/[domain]/[layer]/[name]
-Import Alias: @isa/[domain]/[layer]/[name]
-
-β
Configuration
-----------------
-Test Framework: Vitest with Angular Testing Utilities
-Style: CSS
-Standalone: Yes
-Tags: scope:[domain], type:[layer]
-JUnit Reporter: β
testresults/junit-[library-name].xml
-Cobertura Coverage: β
coverage/libs/[path]/cobertura-coverage.xml
-
-π¦ Import Statement
--------------------
-import { Component } from '@isa/[domain]/[layer]/[name]';
-
-π§ͺ Test Commands
-----------------
-npx nx test [library-name] --skip-nx-cache
-npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
-
-ποΈ Architecture Compliance
---------------------------
-Tags enforce module boundaries via @nx/enforce-module-boundaries
-Run lint to check for violations: npx nx lint [library-name]
-
-π Next Steps
--------------
-1. Develop library features
-2. Write tests using Vitest + Angular Testing Utilities
-3. Add E2E attributes (data-what, data-which) to templates
-4. Update README with usage examples
-5. Follow architecture rules (see eslint.config.js for constraints)
-```
-
-## Error Handling
-
-**Path depth mismatch:**
-- Count directory levels from workspace root
-- Adjust `../` in outputFile and reportsDirectory
-
-**TypeScript errors in vite.config.mts:**
-- Add `// @ts-expect-error` before `defineConfig()`
-
-**Path alias not working:**
-- Check tsconfig.base.json
-- Run `npx nx reset`
-- Restart TypeScript server
-
-**Library already exists:**
-- Check `tsconfig.base.json` for existing path alias
-- Use Grep to search for existing library name
-- Suggest alternative name to user
-
-## References
-
-- docs/guidelines/testing.md (Vitest, JUnit, Cobertura sections)
-- docs/library-reference.md (domain patterns)
-- CLAUDE.md (Library Organization, Testing Framework sections)
-- eslint.config.js (@nx/enforce-module-boundaries configuration)
-- scripts/add-library-tags.js (automatic tagging script)
-- .claude/skills/architecture-enforcer (boundary validation)
-- Nx Angular Library Generator: https://nx.dev/nx-api/angular/generators/library
-- Nx Enforce Module Boundaries: https://nx.dev/nx-api/eslint-plugin/documents/enforce-module-boundaries
+---
+name: library-creator
+description: This skill should be used when creating feature/data-access/ui/util libraries or user says "create library", "new library", "scaffold library". Creates new Angular libraries in ISA-Frontend monorepo with proper Nx configuration, Vitest setup, architectural tags, and path aliases.
+---
+
+# Library Creator
+
+## Overview
+
+Automate the creation of new Angular libraries following ISA-Frontend conventions. This skill handles the complete scaffolding workflow including Nx generation, Vitest configuration with CI/CD integration, architectural tags, path alias verification, and initial validation.
+
+## Required Parameters
+
+Collect these parameters from the user:
+- **domain**: Domain name (oms, remission, checkout, ui, core, shared, utils)
+- **layer**: Layer type (feature, data-access, ui, util)
+- **name**: Library name in kebab-case
+
+## Library Creation Workflow
+
+### Step 1: Validate Input
+
+**Verify Domain:**
+- Use `docs-researcher` to check `docs/library-reference.md`
+- Ensure domain follows existing patterns
+
+**Validate Layer:**
+- Must be one of: feature, data-access, ui, util
+
+**Check Name:**
+- Must be kebab-case
+- Must not conflict with existing libraries
+
+**Determine Path Depth:**
+- 3 levels: `libs/domain/layer/name` β `../../../`
+- 4 levels: `libs/domain/type/layer/name` β `../../../../`
+
+### Step 2: Run Dry-Run
+
+Execute Nx generator with `--dry-run` to preview changes:
+
+```bash
+npx nx generate @nx/angular:library \
+ --name=[domain]-[layer]-[name] \
+ --directory=libs/[domain]/[layer]/[name] \
+ --importPath=@isa/[domain]/[layer]/[name] \
+ --prefix=[domain] \
+ --style=css \
+ --unitTestRunner=vitest \
+ --standalone=true \
+ --skipTests=false \
+ --dry-run
+```
+
+Review output with user before proceeding.
+
+### Step 3: Generate Library
+
+Execute without `--dry-run`:
+
+```bash
+npx nx generate @nx/angular:library \
+ --name=[domain]-[layer]-[name] \
+ --directory=libs/[domain]/[layer]/[name] \
+ --importPath=@isa/[domain]/[layer]/[name] \
+ --prefix=[domain] \
+ --style=css \
+ --unitTestRunner=vitest \
+ --standalone=true \
+ --skipTests=false
+```
+
+### Step 4: Add Architectural Tags
+
+**CRITICAL**: Immediately after library generation, add proper tags to `project.json` for `@nx/enforce-module-boundaries`.
+
+Run the tagging script:
+```bash
+node scripts/add-library-tags.js
+```
+
+Or manually add tags to `libs/[domain]/[layer]/[name]/project.json`:
+
+```json
+{
+ "name": "[domain]-[layer]-[name]",
+ "tags": [
+ "scope:[domain]",
+ "type:[layer]"
+ ]
+}
+```
+
+**Tag Rules:**
+- **Scope tag**: `scope:[domain]` (e.g., `scope:oms`, `scope:crm`, `scope:ui`, `scope:shared`)
+- **Type tag**: `type:[layer]` (e.g., `type:feature`, `type:data-access`, `type:ui`, `type:util`)
+
+**Examples:**
+- `libs/oms/feature/return-search` β `["scope:oms", "type:feature"]`
+- `libs/ui/buttons` β `["scope:ui", "type:ui"]`
+- `libs/shared/scanner` β `["scope:shared", "type:shared"]`
+- `libs/core/auth` β `["scope:core", "type:core"]`
+
+**Verification:**
+```bash
+# Check tags were added
+cat libs/[domain]/[layer]/[name]/project.json | jq '.tags'
+
+# Should output: ["scope:[domain]", "type:[layer]"]
+```
+
+### Step 5: Configure Vitest with JUnit and Cobertura
+
+Update `libs/[path]/vite.config.mts` with this template:
+
+```typescript
+///
+import { defineConfig } from 'vite';
+import angular from '@analogjs/vite-plugin-angular';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
+
+export default
+// @ts-expect-error - Vitest reporter tuple types have complex inference issues
+defineConfig(() => ({
+ root: __dirname,
+ cacheDir: '../../../node_modules/.vite/libs/[path]',
+ plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
+ test: {
+ watch: false,
+ globals: true,
+ environment: 'jsdom',
+ include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ setupFiles: ['src/test-setup.ts'],
+ reporters: [
+ 'default',
+ ['junit', { outputFile: '../../../testresults/junit-[library-name].xml' }],
+ ],
+ coverage: {
+ reportsDirectory: '../../../coverage/libs/[path]',
+ provider: 'v8' as const,
+ reporter: ['text', 'cobertura'],
+ },
+ },
+}));
+```
+
+**Critical**: Adjust path depth (`../../../` or `../../../../`) based on library location.
+
+### Step 6: Verify Configuration
+
+**Check Path Alias:**
+- Verify `tsconfig.base.json` was updated
+- Should have: `"@isa/[domain]/[layer]/[name]": ["libs/[domain]/[layer]/[name]/src/index.ts"]`
+
+**Run Initial Test:**
+```bash
+npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
+```
+
+**Verify CI/CD Files Created:**
+- JUnit XML: `testresults/junit-[library-name].xml`
+- Cobertura XML: `coverage/libs/[path]/cobertura-coverage.xml`
+
+### Step 7: Create Library README
+
+Use `docs-researcher` to find similar library READMEs, then create comprehensive documentation including:
+- Overview and purpose
+- Installation/import instructions
+- API documentation
+- Usage examples
+- Testing information (Vitest + Angular Testing Utilities)
+
+### Step 8: Update Library Reference
+
+Add entry to `docs/library-reference.md` under appropriate domain:
+
+```markdown
+#### `@isa/[domain]/[layer]/[name]`
+**Path:** `libs/[domain]/[layer]/[name]`
+**Type:** [Feature/Data Access/UI/Util]
+**Testing:** Vitest
+
+[Brief description]
+```
+
+### Step 9: Run Full Validation
+
+Execute validation commands to ensure library is properly configured:
+
+```bash
+# Lint (includes boundary checks)
+npx nx lint [library-name]
+
+# Test with coverage
+npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
+
+# Build (if buildable)
+npx nx build [library-name]
+
+# Dependency graph
+npx nx graph --focus=[library-name]
+```
+
+### Step 10: Generate Creation Report
+
+Provide this structured report to the user:
+
+```
+Library Created Successfully
+============================
+
+Library Name: [domain]-[layer]-[name]
+Path: libs/[domain]/[layer]/[name]
+Import Alias: @isa/[domain]/[layer]/[name]
+
+β
Configuration
+----------------
+Test Framework: Vitest with Angular Testing Utilities
+Style: CSS
+Standalone: Yes
+Tags: scope:[domain], type:[layer]
+JUnit Reporter: β
testresults/junit-[library-name].xml
+Cobertura Coverage: β
coverage/libs/[path]/cobertura-coverage.xml
+
+π¦ Import Statement
+-------------------
+import { Component } from '@isa/[domain]/[layer]/[name]';
+
+π§ͺ Test Commands
+----------------
+npx nx test [library-name] --skip-nx-cache
+npx nx test [library-name] --coverage.enabled=true --skip-nx-cache
+
+ποΈ Architecture Compliance
+--------------------------
+Tags enforce module boundaries via @nx/enforce-module-boundaries
+Run lint to check for violations: npx nx lint [library-name]
+
+π Next Steps
+-------------
+1. Develop library features
+2. Write tests using Vitest + Angular Testing Utilities
+3. Add E2E attributes (data-what, data-which) to templates
+4. Update README with usage examples
+5. Follow architecture rules (see eslint.config.js for constraints)
+```
+
+## Error Handling
+
+**Path depth mismatch:**
+- Count directory levels from workspace root
+- Adjust `../` in outputFile and reportsDirectory
+
+**TypeScript errors in vite.config.mts:**
+- Add `// @ts-expect-error` before `defineConfig()`
+
+**Path alias not working:**
+- Check tsconfig.base.json
+- Run `npx nx reset`
+- Restart TypeScript server
+
+**Library already exists:**
+- Check `tsconfig.base.json` for existing path alias
+- Use Grep to search for existing library name
+- Suggest alternative name to user
+
+## References
+
+- docs/guidelines/testing.md (Vitest, JUnit, Cobertura sections)
+- docs/library-reference.md (domain patterns)
+- CLAUDE.md (Library Organization, Testing Framework sections)
+- eslint.config.js (@nx/enforce-module-boundaries configuration)
+- scripts/add-library-tags.js (automatic tagging script)
+- .claude/skills/architecture-validator (boundary validation)
+- Nx Angular Library Generator: https://nx.dev/nx-api/angular/generators/library
+- Nx Enforce Module Boundaries: https://nx.dev/nx-api/eslint-plugin/documents/enforce-module-boundaries
diff --git a/.claude/skills/logging/SKILL.md b/.claude/skills/logging/SKILL.md
index d3937a374..f498d6b3a 100644
--- a/.claude/skills/logging/SKILL.md
+++ b/.claude/skills/logging/SKILL.md
@@ -1,272 +1,234 @@
----
-name: logging
-description: This skill should be used when working with Angular components, directives, services, pipes, guards, or TypeScript classes. Logging is MANDATORY in all Angular files. Implements @isa/core/logging with logger() factory pattern, appropriate log levels, lazy evaluation for performance, error handling, and avoids console.log and common mistakes.
----
-
-# Logging Helper Skill
-
-Ensures consistent and efficient logging using `@isa/core/logging` library.
-
-## When to Use
-
-- Adding logging to new components/services
-- Refactoring existing logging code
-- Reviewing code for proper logging patterns
-- Debugging logging issues
-
-## Core Principles
-
-### 1. Always Use Factory Pattern
-
-```typescript
-import { logger } from '@isa/core/logging';
-
-// β
DO
-#logger = logger();
-
-// β DON'T
-constructor(private loggingService: LoggingService) {}
-```
-
-### 2. Choose Appropriate Log Levels
-
-- **Trace**: Fine-grained debugging (method entry/exit)
-- **Debug**: Development debugging (variable states)
-- **Info**: Runtime information (user actions, events)
-- **Warn**: Potentially harmful situations
-- **Error**: Errors affecting functionality
-
-### 3. Context Patterns
-
-**Static Context** (component level):
-```typescript
-#logger = logger({ component: 'UserProfileComponent' });
-```
-
-**Dynamic Context** (instance level):
-```typescript
-#logger = logger(() => ({
- userId: this.authService.currentUserId,
- storeId: this.config.storeId
-}));
-```
-
-**Message Context** (use functions for performance):
-```typescript
-// β
Recommended - lazy evaluation
-this.#logger.info('Order processed', () => ({
- orderId: order.id,
- total: order.total,
- timestamp: Date.now()
-}));
-
-// β
Acceptable - static values
-this.#logger.info('Order processed', {
- orderId: order.id,
- status: 'completed'
-});
-```
-
-## Essential Patterns
-
-### Component Logging
-```typescript
-@Component({
- selector: 'app-product-list',
- standalone: true,
-})
-export class ProductListComponent {
- #logger = logger({ component: 'ProductListComponent' });
-
- ngOnInit(): void {
- this.#logger.info('Component initialized');
- }
-
- onAction(id: string): void {
- this.#logger.debug('Action triggered', { id });
- }
-}
-```
-
-### Service Logging
-```typescript
-@Injectable({ providedIn: 'root' })
-export class DataService {
- #logger = logger({ service: 'DataService' });
-
- fetchData(endpoint: string): Observable {
- this.#logger.debug('Fetching data', { endpoint });
-
- return this.http.get(endpoint).pipe(
- tap((data) => this.#logger.info('Data fetched', () => ({
- endpoint,
- size: data.length
- }))),
- catchError((error) => {
- this.#logger.error('Fetch failed', error, () => ({
- endpoint,
- status: error.status
- }));
- return throwError(() => error);
- })
- );
- }
-}
-```
-
-### Error Handling
-```typescript
-try {
- await this.processOrder(orderId);
-} catch (error) {
- this.#logger.error('Order processing failed', error as Error, () => ({
- orderId,
- step: this.currentStep,
- attemptNumber: this.retryCount
- }));
- throw error;
-}
-```
-
-### Hierarchical Context
-```typescript
-@Component({
- providers: [
- provideLoggerContext({ feature: 'checkout', module: 'sales' })
- ]
-})
-export class CheckoutComponent {
- #logger = logger(() => ({ userId: this.userService.currentUserId }));
-
- // Logs include: feature, module, userId + message context
-}
-```
-
-## Common Mistakes to Avoid
-
-```typescript
-// β Don't use console.log
-console.log('User logged in');
-// β
Use logger
-this.#logger.info('User logged in');
-
-// β Don't create expensive context eagerly
-this.#logger.debug('Processing', {
- data: this.computeExpensive() // Always executes
-});
-// β
Use function for lazy evaluation
-this.#logger.debug('Processing', () => ({
- data: this.computeExpensive() // Only if debug enabled
-}));
-
-// β Don't log in tight loops
-for (const item of items) {
- this.#logger.debug('Item', { item });
-}
-// β
Log aggregates
-this.#logger.debug('Batch processed', () => ({
- count: items.length
-}));
-
-// β Don't log sensitive data
-this.#logger.info('User auth', { password: user.password });
-// β
Log safe identifiers only
-this.#logger.info('User auth', { userId: user.id });
-
-// β Don't miss error object
-this.#logger.error('Failed');
-// β
Include error object
-this.#logger.error('Failed', error as Error);
-```
-
-## Configuration
-
-### App Configuration
-```typescript
-// app.config.ts
-import { ApplicationConfig, isDevMode } from '@angular/core';
-import {
- provideLogging, withLogLevel, withSink,
- LogLevel, ConsoleLogSink
-} from '@isa/core/logging';
-
-export const appConfig: ApplicationConfig = {
- providers: [
- provideLogging(
- withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
- withSink(ConsoleLogSink),
- withContext({ app: 'ISA', version: '1.0.0' })
- )
- ]
-};
-```
-
-## Testing
-
-```typescript
-import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
-import { LoggingService } from '@isa/core/logging';
-
-describe('MyComponent', () => {
- const createComponent = createComponentFactory({
- component: MyComponent,
- mocks: [LoggingService]
- });
-
- it('should log error', () => {
- const spectator = createComponent();
- const loggingService = spectator.inject(LoggingService);
-
- spectator.component.riskyOperation();
-
- expect(loggingService.error).toHaveBeenCalledWith(
- expect.any(String),
- expect.any(Error),
- expect.any(Function)
- );
- });
-});
-```
-
-## Code Review Checklist
-
-- [ ] Uses `logger()` factory, not `LoggingService` injection
-- [ ] Appropriate log level for each message
-- [ ] Context functions for expensive operations
-- [ ] No sensitive information (passwords, tokens, PII)
-- [ ] No `console.log` statements
-- [ ] Error logs include error object
-- [ ] No logging in tight loops
-- [ ] Component/service identified in context
-- [ ] E2E attributes on interactive elements
-
-## Quick Reference
-
-```typescript
-// Import
-import { logger, provideLoggerContext } from '@isa/core/logging';
-
-// Create logger
-#logger = logger(); // Basic
-#logger = logger({ component: 'Name' }); // Static context
-#logger = logger(() => ({ id: this.id })); // Dynamic context
-
-// Log messages
-this.#logger.trace('Detailed trace');
-this.#logger.debug('Debug info');
-this.#logger.info('General info', () => ({ key: value }));
-this.#logger.warn('Warning');
-this.#logger.error('Error', error, () => ({ context }));
-
-// Component context
-@Component({
- providers: [provideLoggerContext({ feature: 'users' })]
-})
-```
-
-## Additional Resources
-
-- Full documentation: `libs/core/logging/README.md`
-- Examples: `.claude/skills/logging-helper/examples.md`
-- Quick reference: `.claude/skills/logging-helper/reference.md`
-- Troubleshooting: `.claude/skills/logging-helper/troubleshooting.md`
+---
+name: logging
+description: This skill should be used when working with Angular components, directives, services, pipes, guards, or TypeScript classes. Logging is MANDATORY in all Angular files. Implements @isa/core/logging with logger() factory pattern, appropriate log levels, lazy evaluation for performance, error handling, and avoids console.log and common mistakes.
+---
+
+# Logging
+
+Ensures consistent and efficient logging using `@isa/core/logging` library.
+
+## Core Principles
+
+### 1. Always Use Factory Pattern
+
+```typescript
+import { logger } from '@isa/core/logging';
+
+// β
DO
+#logger = logger();
+
+// β DON'T
+constructor(private loggingService: LoggingService) {}
+```
+
+### 2. Choose Appropriate Log Levels
+
+- **Trace**: Fine-grained debugging (method entry/exit)
+- **Debug**: Development debugging (variable states)
+- **Info**: Runtime information (user actions, events)
+- **Warn**: Potentially harmful situations
+- **Error**: Errors affecting functionality
+
+### 3. Context Patterns
+
+**Static Context** (component level):
+```typescript
+#logger = logger({ component: 'UserProfileComponent' });
+```
+
+**Dynamic Context** (instance level):
+```typescript
+#logger = logger(() => ({
+ userId: this.authService.currentUserId,
+ storeId: this.config.storeId
+}));
+```
+
+**Message Context** (use functions for performance):
+```typescript
+// β
Recommended - lazy evaluation
+this.#logger.info('Order processed', () => ({
+ orderId: order.id,
+ total: order.total,
+ timestamp: Date.now()
+}));
+
+// β
Acceptable - static values
+this.#logger.info('Order processed', {
+ orderId: order.id,
+ status: 'completed'
+});
+```
+
+## Essential Patterns
+
+### Component Logging
+```typescript
+@Component({
+ selector: 'app-product-list',
+ standalone: true,
+})
+export class ProductListComponent {
+ #logger = logger({ component: 'ProductListComponent' });
+
+ ngOnInit(): void {
+ this.#logger.info('Component initialized');
+ }
+
+ onAction(id: string): void {
+ this.#logger.debug('Action triggered', { id });
+ }
+}
+```
+
+### Service Logging
+```typescript
+@Injectable({ providedIn: 'root' })
+export class DataService {
+ #logger = logger({ service: 'DataService' });
+
+ fetchData(endpoint: string): Observable {
+ this.#logger.debug('Fetching data', { endpoint });
+
+ return this.http.get(endpoint).pipe(
+ tap((data) => this.#logger.info('Data fetched', () => ({
+ endpoint,
+ size: data.length
+ }))),
+ catchError((error) => {
+ this.#logger.error('Fetch failed', error, () => ({
+ endpoint,
+ status: error.status
+ }));
+ return throwError(() => error);
+ })
+ );
+ }
+}
+```
+
+### Error Handling
+```typescript
+try {
+ await this.processOrder(orderId);
+} catch (error) {
+ this.#logger.error('Order processing failed', error as Error, () => ({
+ orderId,
+ step: this.currentStep,
+ attemptNumber: this.retryCount
+ }));
+ throw error;
+}
+```
+
+### Hierarchical Context
+```typescript
+@Component({
+ providers: [
+ provideLoggerContext({ feature: 'checkout', module: 'sales' })
+ ]
+})
+export class CheckoutComponent {
+ #logger = logger(() => ({ userId: this.userService.currentUserId }));
+
+ // Logs include: feature, module, userId + message context
+}
+```
+
+## Common Mistakes to Avoid
+
+```typescript
+// β Don't use console.log
+console.log('User logged in');
+// β
Use logger
+this.#logger.info('User logged in');
+
+// β Don't create expensive context eagerly
+this.#logger.debug('Processing', {
+ data: this.computeExpensive() // Always executes
+});
+// β
Use function for lazy evaluation
+this.#logger.debug('Processing', () => ({
+ data: this.computeExpensive() // Only if debug enabled
+}));
+
+// β Don't log in tight loops
+for (const item of items) {
+ this.#logger.debug('Item', { item });
+}
+// β
Log aggregates
+this.#logger.debug('Batch processed', () => ({
+ count: items.length
+}));
+
+// β Don't log sensitive data
+this.#logger.info('User auth', { password: user.password });
+// β
Log safe identifiers only
+this.#logger.info('User auth', { userId: user.id });
+
+// β Don't miss error object
+this.#logger.error('Failed');
+// β
Include error object
+this.#logger.error('Failed', error as Error);
+```
+
+## Configuration
+
+### App Configuration
+```typescript
+// app.config.ts
+import { ApplicationConfig, isDevMode } from '@angular/core';
+import {
+ provideLogging, withLogLevel, withSink,
+ LogLevel, ConsoleLogSink
+} from '@isa/core/logging';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideLogging(
+ withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
+ withSink(ConsoleLogSink),
+ withContext({ app: 'ISA', version: '1.0.0' })
+ )
+ ]
+};
+```
+
+## Testing
+
+```typescript
+import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
+import { LoggingService } from '@isa/core/logging';
+
+describe('MyComponent', () => {
+ const createComponent = createComponentFactory({
+ component: MyComponent,
+ mocks: [LoggingService]
+ });
+
+ it('should log error', () => {
+ const spectator = createComponent();
+ const loggingService = spectator.inject(LoggingService);
+
+ spectator.component.riskyOperation();
+
+ expect(loggingService.error).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.any(Error),
+ expect.any(Function)
+ );
+ });
+});
+```
+
+## Additional Resources
+
+**For more detailed information:**
+
+- **API signatures and patterns**: See [references/api-reference.md](references/api-reference.md) for complete API documentation
+- **Real-world examples**: See [references/examples.md](references/examples.md) for components, services, guards, interceptors, and more
+- **Troubleshooting**: See [references/troubleshooting.md](references/troubleshooting.md) for common issues and solutions
+
+**Project documentation:**
+
+- Full library documentation: `libs/core/logging/README.md`
diff --git a/.claude/skills/logging/reference.md b/.claude/skills/logging/references/api-reference.md
similarity index 95%
rename from .claude/skills/logging/reference.md
rename to .claude/skills/logging/references/api-reference.md
index 54b138865..fa8ee942d 100644
--- a/.claude/skills/logging/reference.md
+++ b/.claude/skills/logging/references/api-reference.md
@@ -1,192 +1,192 @@
-# Logging Quick Reference
-
-## API Signatures
-
-```typescript
-// Factory
-function logger(ctx?: MaybeLoggerContextFn): LoggerApi
-
-// Logger API
-interface LoggerApi {
- trace(message: string, context?: MaybeLoggerContextFn): void;
- debug(message: string, context?: MaybeLoggerContextFn): void;
- info(message: string, context?: MaybeLoggerContextFn): void;
- warn(message: string, context?: MaybeLoggerContextFn): void;
- error(message: string, error?: Error, context?: MaybeLoggerContextFn): void;
-}
-
-// Types
-type MaybeLoggerContextFn = LoggerContext | (() => LoggerContext);
-interface LoggerContext { [key: string]: unknown; }
-```
-
-## Common Patterns
-
-| Pattern | Code |
-|---------|------|
-| Basic logger | `#logger = logger()` |
-| Static context | `#logger = logger({ component: 'Name' })` |
-| Dynamic context | `#logger = logger(() => ({ id: this.id }))` |
-| Log info | `this.#logger.info('Message')` |
-| Log with context | `this.#logger.info('Message', () => ({ key: value }))` |
-| Log error | `this.#logger.error('Error', error)` |
-| Error with context | `this.#logger.error('Error', error, () => ({ id }))` |
-| Component context | `providers: [provideLoggerContext({ feature: 'x' })]` |
-
-## Configuration
-
-```typescript
-// app.config.ts
-import { provideLogging, withLogLevel, withSink, withContext,
- LogLevel, ConsoleLogSink } from '@isa/core/logging';
-
-export const appConfig: ApplicationConfig = {
- providers: [
- provideLogging(
- withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
- withSink(ConsoleLogSink),
- withContext({ app: 'ISA', version: '1.0.0' })
- )
- ]
-};
-```
-
-## Log Levels
-
-| Level | Use Case | Example |
-|-------|----------|---------|
-| `Trace` | Method entry/exit | `this.#logger.trace('Entering processData')` |
-| `Debug` | Development info | `this.#logger.debug('Variable state', () => ({ x }))` |
-| `Info` | Runtime events | `this.#logger.info('User logged in', { userId })` |
-| `Warn` | Warnings | `this.#logger.warn('Deprecated API used')` |
-| `Error` | Errors | `this.#logger.error('Operation failed', error)` |
-| `Off` | Disable logging | `withLogLevel(LogLevel.Off)` |
-
-## Decision Trees
-
-### Context Type Decision
-```
-Value changes at runtime?
-ββ Yes β () => ({ value: this.getValue() })
-ββ No β { value: 'static' }
-
-Computing value is expensive?
-ββ Yes β () => ({ data: this.compute() })
-ββ No β Either works
-```
-
-### Log Level Decision
-```
-Method flow details? β Trace
-Development debug? β Debug
-Runtime information? β Info
-Potential problem? β Warn
-Error occurred? β Error
-```
-
-## Performance Tips
-
-```typescript
-// β
DO: Lazy evaluation
-this.#logger.debug('Data', () => ({
- result: this.expensive() // Only runs if debug enabled
-}));
-
-// β DON'T: Eager evaluation
-this.#logger.debug('Data', {
- result: this.expensive() // Always runs
-});
-
-// β
DO: Log aggregates
-this.#logger.info('Batch done', () => ({ count: items.length }));
-
-// β DON'T: Log in loops
-for (const item of items) {
- this.#logger.debug('Item', { item }); // Performance hit
-}
-```
-
-## Testing
-
-```typescript
-import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
-import { LoggingService } from '@isa/core/logging';
-
-describe('MyComponent', () => {
- const createComponent = createComponentFactory({
- component: MyComponent,
- mocks: [LoggingService]
- });
-
- it('logs error', () => {
- const spectator = createComponent();
- const logger = spectator.inject(LoggingService);
-
- spectator.component.operation();
-
- expect(logger.error).toHaveBeenCalled();
- });
-});
-```
-
-## Custom Sink
-
-```typescript
-import { Injectable } from '@angular/core';
-import { Sink, LogLevel, LoggerContext } from '@isa/core/logging';
-
-@Injectable()
-export class CustomSink implements Sink {
- log(level: LogLevel, message: string, context?: LoggerContext, error?: Error): void {
- // Implementation
- }
-}
-
-// Register
-provideLogging(withSink(CustomSink))
-```
-
-## Sink Function (with DI)
-
-```typescript
-import { inject } from '@angular/core';
-import { SinkFn, LogLevel } from '@isa/core/logging';
-
-export const remoteSink: SinkFn = () => {
- const http = inject(HttpClient);
-
- return (level, message, context, error) => {
- if (level === LogLevel.Error) {
- http.post('/api/logs', { level, message, context, error }).subscribe();
- }
- };
-};
-
-// Register
-provideLogging(withSinkFn(remoteSink))
-```
-
-## Common Imports
-
-```typescript
-// Main imports
-import { logger, provideLoggerContext } from '@isa/core/logging';
-
-// Configuration imports
-import {
- provideLogging,
- withLogLevel,
- withSink,
- withContext,
- LogLevel,
- ConsoleLogSink
-} from '@isa/core/logging';
-
-// Type imports
-import {
- LoggerApi,
- Sink,
- SinkFn,
- LoggerContext
-} from '@isa/core/logging';
-```
+# Logging API Reference
+
+## API Signatures
+
+```typescript
+// Factory
+function logger(ctx?: MaybeLoggerContextFn): LoggerApi
+
+// Logger API
+interface LoggerApi {
+ trace(message: string, context?: MaybeLoggerContextFn): void;
+ debug(message: string, context?: MaybeLoggerContextFn): void;
+ info(message: string, context?: MaybeLoggerContextFn): void;
+ warn(message: string, context?: MaybeLoggerContextFn): void;
+ error(message: string, error?: Error, context?: MaybeLoggerContextFn): void;
+}
+
+// Types
+type MaybeLoggerContextFn = LoggerContext | (() => LoggerContext);
+interface LoggerContext { [key: string]: unknown; }
+```
+
+## Common Patterns
+
+| Pattern | Code |
+|---------|------|
+| Basic logger | `#logger = logger()` |
+| Static context | `#logger = logger({ component: 'Name' })` |
+| Dynamic context | `#logger = logger(() => ({ id: this.id }))` |
+| Log info | `this.#logger.info('Message')` |
+| Log with context | `this.#logger.info('Message', () => ({ key: value }))` |
+| Log error | `this.#logger.error('Error', error)` |
+| Error with context | `this.#logger.error('Error', error, () => ({ id }))` |
+| Component context | `providers: [provideLoggerContext({ feature: 'x' })]` |
+
+## Configuration
+
+```typescript
+// app.config.ts
+import { provideLogging, withLogLevel, withSink, withContext,
+ LogLevel, ConsoleLogSink } from '@isa/core/logging';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideLogging(
+ withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
+ withSink(ConsoleLogSink),
+ withContext({ app: 'ISA', version: '1.0.0' })
+ )
+ ]
+};
+```
+
+## Log Levels
+
+| Level | Use Case | Example |
+|-------|----------|---------|
+| `Trace` | Method entry/exit | `this.#logger.trace('Entering processData')` |
+| `Debug` | Development info | `this.#logger.debug('Variable state', () => ({ x }))` |
+| `Info` | Runtime events | `this.#logger.info('User logged in', { userId })` |
+| `Warn` | Warnings | `this.#logger.warn('Deprecated API used')` |
+| `Error` | Errors | `this.#logger.error('Operation failed', error)` |
+| `Off` | Disable logging | `withLogLevel(LogLevel.Off)` |
+
+## Decision Trees
+
+### Context Type Decision
+```
+Value changes at runtime?
+ββ Yes β () => ({ value: this.getValue() })
+ββ No β { value: 'static' }
+
+Computing value is expensive?
+ββ Yes β () => ({ data: this.compute() })
+ββ No β Either works
+```
+
+### Log Level Decision
+```
+Method flow details? β Trace
+Development debug? β Debug
+Runtime information? β Info
+Potential problem? β Warn
+Error occurred? β Error
+```
+
+## Performance Tips
+
+```typescript
+// β
DO: Lazy evaluation
+this.#logger.debug('Data', () => ({
+ result: this.expensive() // Only runs if debug enabled
+}));
+
+// β DON'T: Eager evaluation
+this.#logger.debug('Data', {
+ result: this.expensive() // Always runs
+});
+
+// β
DO: Log aggregates
+this.#logger.info('Batch done', () => ({ count: items.length }));
+
+// β DON'T: Log in loops
+for (const item of items) {
+ this.#logger.debug('Item', { item }); // Performance hit
+}
+```
+
+## Testing
+
+```typescript
+import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
+import { LoggingService } from '@isa/core/logging';
+
+describe('MyComponent', () => {
+ const createComponent = createComponentFactory({
+ component: MyComponent,
+ mocks: [LoggingService]
+ });
+
+ it('logs error', () => {
+ const spectator = createComponent();
+ const logger = spectator.inject(LoggingService);
+
+ spectator.component.operation();
+
+ expect(logger.error).toHaveBeenCalled();
+ });
+});
+```
+
+## Custom Sink
+
+```typescript
+import { Injectable } from '@angular/core';
+import { Sink, LogLevel, LoggerContext } from '@isa/core/logging';
+
+@Injectable()
+export class CustomSink implements Sink {
+ log(level: LogLevel, message: string, context?: LoggerContext, error?: Error): void {
+ // Implementation
+ }
+}
+
+// Register
+provideLogging(withSink(CustomSink))
+```
+
+## Sink Function (with DI)
+
+```typescript
+import { inject } from '@angular/core';
+import { SinkFn, LogLevel } from '@isa/core/logging';
+
+export const remoteSink: SinkFn = () => {
+ const http = inject(HttpClient);
+
+ return (level, message, context, error) => {
+ if (level === LogLevel.Error) {
+ http.post('/api/logs', { level, message, context, error }).subscribe();
+ }
+ };
+};
+
+// Register
+provideLogging(withSinkFn(remoteSink))
+```
+
+## Common Imports
+
+```typescript
+// Main imports
+import { logger, provideLoggerContext } from '@isa/core/logging';
+
+// Configuration imports
+import {
+ provideLogging,
+ withLogLevel,
+ withSink,
+ withContext,
+ LogLevel,
+ ConsoleLogSink
+} from '@isa/core/logging';
+
+// Type imports
+import {
+ LoggerApi,
+ Sink,
+ SinkFn,
+ LoggerContext
+} from '@isa/core/logging';
+```
diff --git a/.claude/skills/logging/examples.md b/.claude/skills/logging/references/examples.md
similarity index 96%
rename from .claude/skills/logging/examples.md
rename to .claude/skills/logging/references/examples.md
index 90e6d114c..5f19d9450 100644
--- a/.claude/skills/logging/examples.md
+++ b/.claude/skills/logging/references/examples.md
@@ -1,350 +1,350 @@
-# Logging Examples
-
-Concise real-world examples of logging patterns.
-
-## 1. Component with Observable
-
-```typescript
-import { Component, OnInit } from '@angular/core';
-import { logger } from '@isa/core/logging';
-
-@Component({
- selector: 'app-product-list',
- standalone: true,
-})
-export class ProductListComponent implements OnInit {
- #logger = logger({ component: 'ProductListComponent' });
-
- constructor(private productService: ProductService) {}
-
- ngOnInit(): void {
- this.#logger.info('Component initialized');
- this.loadProducts();
- }
-
- private loadProducts(): void {
- this.productService.getProducts().subscribe({
- next: (products) => {
- this.#logger.info('Products loaded', () => ({ count: products.length }));
- },
- error: (error) => {
- this.#logger.error('Failed to load products', error);
- }
- });
- }
-}
-```
-
-## 2. Service with HTTP
-
-```typescript
-import { Injectable, inject } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { logger } from '@isa/core/logging';
-import { catchError, tap } from 'rxjs/operators';
-
-@Injectable({ providedIn: 'root' })
-export class OrderService {
- private http = inject(HttpClient);
- #logger = logger({ service: 'OrderService' });
-
- getOrder(id: string): Observable {
- this.#logger.debug('Fetching order', { id });
-
- return this.http.get(`/api/orders/${id}`).pipe(
- tap((order) => this.#logger.info('Order fetched', () => ({
- id,
- status: order.status
- }))),
- catchError((error) => {
- this.#logger.error('Fetch failed', error, () => ({ id, status: error.status }));
- throw error;
- })
- );
- }
-}
-```
-
-## 3. Hierarchical Context
-
-```typescript
-import { Component } from '@angular/core';
-import { logger, provideLoggerContext } from '@isa/core/logging';
-
-@Component({
- selector: 'oms-return-process',
- standalone: true,
- providers: [
- provideLoggerContext({ feature: 'returns', module: 'oms' })
- ],
-})
-export class ReturnProcessComponent {
- #logger = logger(() => ({
- processId: this.currentProcessId,
- step: this.currentStep
- }));
-
- private currentProcessId = crypto.randomUUID();
- private currentStep = 1;
-
- startProcess(orderId: string): void {
- // Logs include: feature, module, processId, step, orderId
- this.#logger.info('Process started', { orderId });
- }
-}
-```
-
-## 4. NgRx Effect
-
-```typescript
-import { Injectable } from '@angular/core';
-import { Actions, createEffect, ofType } from '@ngrx/effects';
-import { logger } from '@isa/core/logging';
-import { map, catchError, tap } from 'rxjs/operators';
-import { of } from 'rxjs';
-
-@Injectable()
-export class OrdersEffects {
- #logger = logger({ effect: 'OrdersEffects' });
-
- loadOrders$ = createEffect(() =>
- this.actions$.pipe(
- ofType(OrdersActions.loadOrders),
- tap((action) => this.#logger.debug('Loading orders', () => ({
- page: action.page
- }))),
- mergeMap((action) =>
- this.orderService.getOrders(action.filters).pipe(
- map((orders) => {
- this.#logger.info('Orders loaded', () => ({ count: orders.length }));
- return OrdersActions.loadOrdersSuccess({ orders });
- }),
- catchError((error) => {
- this.#logger.error('Load failed', error);
- return of(OrdersActions.loadOrdersFailure({ error }));
- })
- )
- )
- )
- );
-
- constructor(
- private actions$: Actions,
- private orderService: OrderService
- ) {}
-}
-```
-
-## 5. Guard with Authorization
-
-```typescript
-import { inject } from '@angular/core';
-import { CanActivateFn, Router } from '@angular/router';
-import { logger } from '@isa/core/logging';
-
-export const authGuard: CanActivateFn = (route, state) => {
- const authService = inject(AuthService);
- const router = inject(Router);
- const log = logger({ guard: 'AuthGuard' });
-
- if (authService.isAuthenticated()) {
- log.debug('Access granted', () => ({ route: state.url }));
- return true;
- }
-
- log.warn('Access denied', () => ({
- attemptedRoute: state.url,
- redirectTo: '/login'
- }));
- return router.createUrlTree(['/login']);
-};
-```
-
-## 6. HTTP Interceptor
-
-```typescript
-import { HttpInterceptorFn } from '@angular/common/http';
-import { inject } from '@angular/core';
-import { tap, catchError } from 'rxjs/operators';
-import { LoggingService } from '@isa/core/logging';
-
-export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
- const loggingService = inject(LoggingService);
- const startTime = performance.now();
-
- loggingService.debug('HTTP Request', () => ({
- method: req.method,
- url: req.url
- }));
-
- return next(req).pipe(
- tap((event) => {
- if (event.type === HttpEventType.Response) {
- loggingService.info('HTTP Response', () => ({
- method: req.method,
- url: req.url,
- status: event.status,
- duration: `${(performance.now() - startTime).toFixed(2)}ms`
- }));
- }
- }),
- catchError((error) => {
- loggingService.error('HTTP Error', error, () => ({
- method: req.method,
- url: req.url,
- status: error.status
- }));
- return throwError(() => error);
- })
- );
-};
-```
-
-## 7. Form Validation
-
-```typescript
-import { Component, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { logger } from '@isa/core/logging';
-
-@Component({
- selector: 'shared-user-form',
- standalone: true,
-})
-export class UserFormComponent implements OnInit {
- #logger = logger({ component: 'UserFormComponent' });
- form!: FormGroup;
-
- constructor(private fb: FormBuilder) {}
-
- ngOnInit(): void {
- this.form = this.fb.group({
- name: ['', Validators.required],
- email: ['', [Validators.required, Validators.email]]
- });
- }
-
- onSubmit(): void {
- if (this.form.invalid) {
- this.#logger.warn('Invalid form submission', () => ({
- errors: this.getFormErrors()
- }));
- return;
- }
-
- this.#logger.info('Form submitted');
- }
-
- private getFormErrors(): Record {
- const errors: Record = {};
- Object.keys(this.form.controls).forEach((key) => {
- const control = this.form.get(key);
- if (control?.errors) errors[key] = control.errors;
- });
- return errors;
- }
-}
-```
-
-## 8. Async Progress Tracking
-
-```typescript
-import { Injectable } from '@angular/core';
-import { logger } from '@isa/core/logging';
-import { Observable } from 'rxjs';
-import { tap } from 'rxjs/operators';
-
-@Injectable({ providedIn: 'root' })
-export class ImportService {
- #logger = logger({ service: 'ImportService' });
-
- importData(file: File): Observable {
- const importId = crypto.randomUUID();
-
- this.#logger.info('Import started', () => ({
- importId,
- fileName: file.name,
- fileSize: file.size
- }));
-
- return this.processImport(file).pipe(
- tap((progress) => {
- if (progress % 25 === 0) {
- this.#logger.debug('Import progress', () => ({
- importId,
- progress: `${progress}%`
- }));
- }
- }),
- tap({
- complete: () => this.#logger.info('Import completed', { importId }),
- error: (error) => this.#logger.error('Import failed', error, { importId })
- })
- );
- }
-
- private processImport(file: File): Observable {
- // Implementation
- }
-}
-```
-
-## 9. Global Error Handler
-
-```typescript
-import { Injectable, ErrorHandler } from '@angular/core';
-import { logger } from '@isa/core/logging';
-
-@Injectable()
-export class GlobalErrorHandler implements ErrorHandler {
- #logger = logger({ handler: 'GlobalErrorHandler' });
-
- handleError(error: Error): void {
- this.#logger.error('Uncaught error', error, () => ({
- url: window.location.href,
- userAgent: navigator.userAgent,
- timestamp: new Date().toISOString()
- }));
- }
-}
-```
-
-## 10. WebSocket Component
-
-```typescript
-import { Component, OnInit, OnDestroy } from '@angular/core';
-import { logger } from '@isa/core/logging';
-import { Subject, takeUntil } from 'rxjs';
-
-@Component({
- selector: 'oms-live-orders',
- standalone: true,
-})
-export class LiveOrdersComponent implements OnInit, OnDestroy {
- #logger = logger({ component: 'LiveOrdersComponent' });
- private destroy$ = new Subject();
-
- constructor(private wsService: WebSocketService) {}
-
- ngOnInit(): void {
- this.#logger.info('Connecting to WebSocket');
-
- this.wsService.connect('orders').pipe(
- takeUntil(this.destroy$)
- ).subscribe({
- next: (msg) => this.#logger.debug('Message received', () => ({
- type: msg.type,
- orderId: msg.orderId
- })),
- error: (error) => this.#logger.error('WebSocket error', error),
- complete: () => this.#logger.info('WebSocket closed')
- });
- }
-
- ngOnDestroy(): void {
- this.#logger.debug('Component destroyed');
- this.destroy$.next();
- this.destroy$.complete();
- }
-}
-```
+# Logging Examples
+
+Concise real-world examples of logging patterns.
+
+## 1. Component with Observable
+
+```typescript
+import { Component, OnInit } from '@angular/core';
+import { logger } from '@isa/core/logging';
+
+@Component({
+ selector: 'app-product-list',
+ standalone: true,
+})
+export class ProductListComponent implements OnInit {
+ #logger = logger({ component: 'ProductListComponent' });
+
+ constructor(private productService: ProductService) {}
+
+ ngOnInit(): void {
+ this.#logger.info('Component initialized');
+ this.loadProducts();
+ }
+
+ private loadProducts(): void {
+ this.productService.getProducts().subscribe({
+ next: (products) => {
+ this.#logger.info('Products loaded', () => ({ count: products.length }));
+ },
+ error: (error) => {
+ this.#logger.error('Failed to load products', error);
+ }
+ });
+ }
+}
+```
+
+## 2. Service with HTTP
+
+```typescript
+import { Injectable, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { logger } from '@isa/core/logging';
+import { catchError, tap } from 'rxjs/operators';
+
+@Injectable({ providedIn: 'root' })
+export class OrderService {
+ private http = inject(HttpClient);
+ #logger = logger({ service: 'OrderService' });
+
+ getOrder(id: string): Observable {
+ this.#logger.debug('Fetching order', { id });
+
+ return this.http.get(`/api/orders/${id}`).pipe(
+ tap((order) => this.#logger.info('Order fetched', () => ({
+ id,
+ status: order.status
+ }))),
+ catchError((error) => {
+ this.#logger.error('Fetch failed', error, () => ({ id, status: error.status }));
+ throw error;
+ })
+ );
+ }
+}
+```
+
+## 3. Hierarchical Context
+
+```typescript
+import { Component } from '@angular/core';
+import { logger, provideLoggerContext } from '@isa/core/logging';
+
+@Component({
+ selector: 'oms-return-process',
+ standalone: true,
+ providers: [
+ provideLoggerContext({ feature: 'returns', module: 'oms' })
+ ],
+})
+export class ReturnProcessComponent {
+ #logger = logger(() => ({
+ processId: this.currentProcessId,
+ step: this.currentStep
+ }));
+
+ private currentProcessId = crypto.randomUUID();
+ private currentStep = 1;
+
+ startProcess(orderId: string): void {
+ // Logs include: feature, module, processId, step, orderId
+ this.#logger.info('Process started', { orderId });
+ }
+}
+```
+
+## 4. NgRx Effect
+
+```typescript
+import { Injectable } from '@angular/core';
+import { Actions, createEffect, ofType } from '@ngrx/effects';
+import { logger } from '@isa/core/logging';
+import { map, catchError, tap } from 'rxjs/operators';
+import { of } from 'rxjs';
+
+@Injectable()
+export class OrdersEffects {
+ #logger = logger({ effect: 'OrdersEffects' });
+
+ loadOrders$ = createEffect(() =>
+ this.actions$.pipe(
+ ofType(OrdersActions.loadOrders),
+ tap((action) => this.#logger.debug('Loading orders', () => ({
+ page: action.page
+ }))),
+ mergeMap((action) =>
+ this.orderService.getOrders(action.filters).pipe(
+ map((orders) => {
+ this.#logger.info('Orders loaded', () => ({ count: orders.length }));
+ return OrdersActions.loadOrdersSuccess({ orders });
+ }),
+ catchError((error) => {
+ this.#logger.error('Load failed', error);
+ return of(OrdersActions.loadOrdersFailure({ error }));
+ })
+ )
+ )
+ )
+ );
+
+ constructor(
+ private actions$: Actions,
+ private orderService: OrderService
+ ) {}
+}
+```
+
+## 5. Guard with Authorization
+
+```typescript
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import { logger } from '@isa/core/logging';
+
+export const authGuard: CanActivateFn = (route, state) => {
+ const authService = inject(AuthService);
+ const router = inject(Router);
+ const log = logger({ guard: 'AuthGuard' });
+
+ if (authService.isAuthenticated()) {
+ log.debug('Access granted', () => ({ route: state.url }));
+ return true;
+ }
+
+ log.warn('Access denied', () => ({
+ attemptedRoute: state.url,
+ redirectTo: '/login'
+ }));
+ return router.createUrlTree(['/login']);
+};
+```
+
+## 6. HTTP Interceptor
+
+```typescript
+import { HttpInterceptorFn } from '@angular/common/http';
+import { inject } from '@angular/core';
+import { tap, catchError } from 'rxjs/operators';
+import { LoggingService } from '@isa/core/logging';
+
+export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
+ const loggingService = inject(LoggingService);
+ const startTime = performance.now();
+
+ loggingService.debug('HTTP Request', () => ({
+ method: req.method,
+ url: req.url
+ }));
+
+ return next(req).pipe(
+ tap((event) => {
+ if (event.type === HttpEventType.Response) {
+ loggingService.info('HTTP Response', () => ({
+ method: req.method,
+ url: req.url,
+ status: event.status,
+ duration: `${(performance.now() - startTime).toFixed(2)}ms`
+ }));
+ }
+ }),
+ catchError((error) => {
+ loggingService.error('HTTP Error', error, () => ({
+ method: req.method,
+ url: req.url,
+ status: error.status
+ }));
+ return throwError(() => error);
+ })
+ );
+};
+```
+
+## 7. Form Validation
+
+```typescript
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { logger } from '@isa/core/logging';
+
+@Component({
+ selector: 'shared-user-form',
+ standalone: true,
+})
+export class UserFormComponent implements OnInit {
+ #logger = logger({ component: 'UserFormComponent' });
+ form!: FormGroup;
+
+ constructor(private fb: FormBuilder) {}
+
+ ngOnInit(): void {
+ this.form = this.fb.group({
+ name: ['', Validators.required],
+ email: ['', [Validators.required, Validators.email]]
+ });
+ }
+
+ onSubmit(): void {
+ if (this.form.invalid) {
+ this.#logger.warn('Invalid form submission', () => ({
+ errors: this.getFormErrors()
+ }));
+ return;
+ }
+
+ this.#logger.info('Form submitted');
+ }
+
+ private getFormErrors(): Record {
+ const errors: Record = {};
+ Object.keys(this.form.controls).forEach((key) => {
+ const control = this.form.get(key);
+ if (control?.errors) errors[key] = control.errors;
+ });
+ return errors;
+ }
+}
+```
+
+## 8. Async Progress Tracking
+
+```typescript
+import { Injectable } from '@angular/core';
+import { logger } from '@isa/core/logging';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+@Injectable({ providedIn: 'root' })
+export class ImportService {
+ #logger = logger({ service: 'ImportService' });
+
+ importData(file: File): Observable {
+ const importId = crypto.randomUUID();
+
+ this.#logger.info('Import started', () => ({
+ importId,
+ fileName: file.name,
+ fileSize: file.size
+ }));
+
+ return this.processImport(file).pipe(
+ tap((progress) => {
+ if (progress % 25 === 0) {
+ this.#logger.debug('Import progress', () => ({
+ importId,
+ progress: `${progress}%`
+ }));
+ }
+ }),
+ tap({
+ complete: () => this.#logger.info('Import completed', { importId }),
+ error: (error) => this.#logger.error('Import failed', error, { importId })
+ })
+ );
+ }
+
+ private processImport(file: File): Observable {
+ // Implementation
+ }
+}
+```
+
+## 9. Global Error Handler
+
+```typescript
+import { Injectable, ErrorHandler } from '@angular/core';
+import { logger } from '@isa/core/logging';
+
+@Injectable()
+export class GlobalErrorHandler implements ErrorHandler {
+ #logger = logger({ handler: 'GlobalErrorHandler' });
+
+ handleError(error: Error): void {
+ this.#logger.error('Uncaught error', error, () => ({
+ url: window.location.href,
+ userAgent: navigator.userAgent,
+ timestamp: new Date().toISOString()
+ }));
+ }
+}
+```
+
+## 10. WebSocket Component
+
+```typescript
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { logger } from '@isa/core/logging';
+import { Subject, takeUntil } from 'rxjs';
+
+@Component({
+ selector: 'oms-live-orders',
+ standalone: true,
+})
+export class LiveOrdersComponent implements OnInit, OnDestroy {
+ #logger = logger({ component: 'LiveOrdersComponent' });
+ private destroy$ = new Subject();
+
+ constructor(private wsService: WebSocketService) {}
+
+ ngOnInit(): void {
+ this.#logger.info('Connecting to WebSocket');
+
+ this.wsService.connect('orders').pipe(
+ takeUntil(this.destroy$)
+ ).subscribe({
+ next: (msg) => this.#logger.debug('Message received', () => ({
+ type: msg.type,
+ orderId: msg.orderId
+ })),
+ error: (error) => this.#logger.error('WebSocket error', error),
+ complete: () => this.#logger.info('WebSocket closed')
+ });
+ }
+
+ ngOnDestroy(): void {
+ this.#logger.debug('Component destroyed');
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+}
+```
diff --git a/.claude/skills/logging/troubleshooting.md b/.claude/skills/logging/references/troubleshooting.md
similarity index 95%
rename from .claude/skills/logging/troubleshooting.md
rename to .claude/skills/logging/references/troubleshooting.md
index 1df623b13..ed9612cc3 100644
--- a/.claude/skills/logging/troubleshooting.md
+++ b/.claude/skills/logging/references/troubleshooting.md
@@ -1,235 +1,235 @@
-# Logging Troubleshooting
-
-## 1. Logs Not Appearing
-
-**Problem:** Logger called but nothing in console.
-
-**Solutions:**
-```typescript
-// Check log level
-provideLogging(
- withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn)
-)
-
-// Add sink
-provideLogging(
- withLogLevel(LogLevel.Debug),
- withSink(ConsoleLogSink) // Required!
-)
-
-// Verify configuration in app.config.ts
-export const appConfig: ApplicationConfig = {
- providers: [
- provideLogging(...) // Must be present
- ]
-};
-```
-
-## 2. NullInjectorError
-
-**Error:** `NullInjectorError: No provider for LoggingService!`
-
-**Solution:**
-```typescript
-// app.config.ts
-import { provideLogging, withLogLevel, withSink,
- LogLevel, ConsoleLogSink } from '@isa/core/logging';
-
-export const appConfig: ApplicationConfig = {
- providers: [
- provideLogging(
- withLogLevel(LogLevel.Debug),
- withSink(ConsoleLogSink)
- )
- ]
-};
-```
-
-## 3. Context Not Showing
-
-**Problem:** Context passed but doesn't appear.
-
-**Check:**
-```typescript
-// β
Both work:
-this.#logger.info('Message', () => ({ id: '123' })); // Function
-this.#logger.info('Message', { id: '123' }); // Object
-
-// β Common mistake:
-const ctx = { id: '123' };
-this.#logger.info('Message', ctx); // Actually works!
-
-// Verify hierarchical merge:
-// Global β Component β Instance β Message
-```
-
-## 4. Performance Issues
-
-**Problem:** Slow when debug logging enabled.
-
-**Solutions:**
-```typescript
-// β
Use lazy evaluation
-this.#logger.debug('Data', () => ({
- expensive: this.compute() // Only if debug enabled
-}));
-
-// β
Reduce log frequency
-this.#logger.debug('Batch', () => ({
- count: items.length // Not each item
-}));
-
-// β
Increase production level
-provideLogging(
- withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn)
-)
-```
-
-## 5. Error Object Not Logged
-
-**Problem:** Error shows as `[object Object]`.
-
-**Solution:**
-```typescript
-// β Wrong
-this.#logger.error('Failed', { error }); // Don't wrap in object
-
-// β
Correct
-this.#logger.error('Failed', error as Error, () => ({
- additionalContext: 'value'
-}));
-```
-
-## 6. TypeScript Errors
-
-**Error:** `Type 'X' is not assignable to 'MaybeLoggerContextFn'`
-
-**Solution:**
-```typescript
-// β Wrong type
-this.#logger.info('Message', 'string'); // Invalid
-
-// β
Correct types
-this.#logger.info('Message', { key: 'value' });
-this.#logger.info('Message', () => ({ key: 'value' }));
-```
-
-## 7. Logs in Tests
-
-**Problem:** Test output cluttered with logs.
-
-**Solutions:**
-```typescript
-// Mock logging service
-import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
-import { LoggingService } from '@isa/core/logging';
-
-const createComponent = createComponentFactory({
- component: MyComponent,
- mocks: [LoggingService] // Mocks all log methods
-});
-
-// Or disable in tests
-TestBed.configureTestingModule({
- providers: [
- provideLogging(withLogLevel(LogLevel.Off))
- ]
-});
-```
-
-## 8. Undefined Property Error
-
-**Error:** `Cannot read property 'X' of undefined`
-
-**Problem:** Accessing uninitialized property in logger context.
-
-**Solutions:**
-```typescript
-// β Problem
-#logger = logger(() => ({
- userId: this.userService.currentUserId // May be undefined
-}));
-
-// β
Solution 1: Optional chaining
-#logger = logger(() => ({
- userId: this.userService?.currentUserId ?? 'unknown'
-}));
-
-// β
Solution 2: Delay access
-ngOnInit() {
- this.#logger.info('Init', () => ({
- userId: this.userService.currentUserId // Safe here
- }));
-}
-```
-
-## 9. Circular Dependency
-
-**Error:** `NG0200: Circular dependency in DI detected`
-
-**Cause:** Service A β β Service B both inject LoggingService.
-
-**Solution:**
-```typescript
-// β Creates circular dependency
-constructor(private loggingService: LoggingService) {}
-
-// β
Use factory (no circular dependency)
-#logger = logger({ service: 'MyService' });
-```
-
-## 10. Custom Sink Not Working
-
-**Problem:** Sink registered but never called.
-
-**Solutions:**
-```typescript
-// β
Correct registration
-provideLogging(
- withSink(MySink) // Add to config
-)
-
-// β
Correct signature
-export class MySink implements Sink {
- log(
- level: LogLevel,
- message: string,
- context?: LoggerContext,
- error?: Error
- ): void {
- // Implementation
- }
-}
-
-// β
Sink function must return function
-export const mySinkFn: SinkFn = () => {
- const http = inject(HttpClient);
- return (level, message, context, error) => {
- // Implementation
- };
-};
-```
-
-## Quick Diagnostics
-
-```typescript
-// Enable all logs temporarily
-provideLogging(withLogLevel(LogLevel.Trace))
-
-// Check imports
-import { logger } from '@isa/core/logging'; // β
Correct
-import { logger } from '@isa/core/logging/src/lib/logger.factory'; // β Wrong
-
-// Verify console filters in browser DevTools
-// Ensure Info, Debug, Warnings are enabled
-```
-
-## Common Error Messages
-
-| Error | Cause | Fix |
-|-------|-------|-----|
-| `NullInjectorError: LoggingService` | Missing config | Add `provideLogging()` |
-| `Type 'X' not assignable` | Wrong context type | Use object or function |
-| `Cannot read property 'X'` | Undefined property | Use optional chaining |
-| `Circular dependency` | Service injection | Use `logger()` factory |
-| Stack overflow | Infinite loop in context | Don't call logger in context |
+# Logging Troubleshooting
+
+## 1. Logs Not Appearing
+
+**Problem:** Logger called but nothing in console.
+
+**Solutions:**
+```typescript
+// Check log level
+provideLogging(
+ withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn)
+)
+
+// Add sink
+provideLogging(
+ withLogLevel(LogLevel.Debug),
+ withSink(ConsoleLogSink) // Required!
+)
+
+// Verify configuration in app.config.ts
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideLogging(...) // Must be present
+ ]
+};
+```
+
+## 2. NullInjectorError
+
+**Error:** `NullInjectorError: No provider for LoggingService!`
+
+**Solution:**
+```typescript
+// app.config.ts
+import { provideLogging, withLogLevel, withSink,
+ LogLevel, ConsoleLogSink } from '@isa/core/logging';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideLogging(
+ withLogLevel(LogLevel.Debug),
+ withSink(ConsoleLogSink)
+ )
+ ]
+};
+```
+
+## 3. Context Not Showing
+
+**Problem:** Context passed but doesn't appear.
+
+**Check:**
+```typescript
+// β
Both work:
+this.#logger.info('Message', () => ({ id: '123' })); // Function
+this.#logger.info('Message', { id: '123' }); // Object
+
+// β Common mistake:
+const ctx = { id: '123' };
+this.#logger.info('Message', ctx); // Actually works!
+
+// Verify hierarchical merge:
+// Global β Component β Instance β Message
+```
+
+## 4. Performance Issues
+
+**Problem:** Slow when debug logging enabled.
+
+**Solutions:**
+```typescript
+// β
Use lazy evaluation
+this.#logger.debug('Data', () => ({
+ expensive: this.compute() // Only if debug enabled
+}));
+
+// β
Reduce log frequency
+this.#logger.debug('Batch', () => ({
+ count: items.length // Not each item
+}));
+
+// β
Increase production level
+provideLogging(
+ withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn)
+)
+```
+
+## 5. Error Object Not Logged
+
+**Problem:** Error shows as `[object Object]`.
+
+**Solution:**
+```typescript
+// β Wrong
+this.#logger.error('Failed', { error }); // Don't wrap in object
+
+// β
Correct
+this.#logger.error('Failed', error as Error, () => ({
+ additionalContext: 'value'
+}));
+```
+
+## 6. TypeScript Errors
+
+**Error:** `Type 'X' is not assignable to 'MaybeLoggerContextFn'`
+
+**Solution:**
+```typescript
+// β Wrong type
+this.#logger.info('Message', 'string'); // Invalid
+
+// β
Correct types
+this.#logger.info('Message', { key: 'value' });
+this.#logger.info('Message', () => ({ key: 'value' }));
+```
+
+## 7. Logs in Tests
+
+**Problem:** Test output cluttered with logs.
+
+**Solutions:**
+```typescript
+// Mock logging service
+import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
+import { LoggingService } from '@isa/core/logging';
+
+const createComponent = createComponentFactory({
+ component: MyComponent,
+ mocks: [LoggingService] // Mocks all log methods
+});
+
+// Or disable in tests
+TestBed.configureTestingModule({
+ providers: [
+ provideLogging(withLogLevel(LogLevel.Off))
+ ]
+});
+```
+
+## 8. Undefined Property Error
+
+**Error:** `Cannot read property 'X' of undefined`
+
+**Problem:** Accessing uninitialized property in logger context.
+
+**Solutions:**
+```typescript
+// β Problem
+#logger = logger(() => ({
+ userId: this.userService.currentUserId // May be undefined
+}));
+
+// β
Solution 1: Optional chaining
+#logger = logger(() => ({
+ userId: this.userService?.currentUserId ?? 'unknown'
+}));
+
+// β
Solution 2: Delay access
+ngOnInit() {
+ this.#logger.info('Init', () => ({
+ userId: this.userService.currentUserId // Safe here
+ }));
+}
+```
+
+## 9. Circular Dependency
+
+**Error:** `NG0200: Circular dependency in DI detected`
+
+**Cause:** Service A β β Service B both inject LoggingService.
+
+**Solution:**
+```typescript
+// β Creates circular dependency
+constructor(private loggingService: LoggingService) {}
+
+// β
Use factory (no circular dependency)
+#logger = logger({ service: 'MyService' });
+```
+
+## 10. Custom Sink Not Working
+
+**Problem:** Sink registered but never called.
+
+**Solutions:**
+```typescript
+// β
Correct registration
+provideLogging(
+ withSink(MySink) // Add to config
+)
+
+// β
Correct signature
+export class MySink implements Sink {
+ log(
+ level: LogLevel,
+ message: string,
+ context?: LoggerContext,
+ error?: Error
+ ): void {
+ // Implementation
+ }
+}
+
+// β
Sink function must return function
+export const mySinkFn: SinkFn = () => {
+ const http = inject(HttpClient);
+ return (level, message, context, error) => {
+ // Implementation
+ };
+};
+```
+
+## Quick Diagnostics
+
+```typescript
+// Enable all logs temporarily
+provideLogging(withLogLevel(LogLevel.Trace))
+
+// Check imports
+import { logger } from '@isa/core/logging'; // β
Correct
+import { logger } from '@isa/core/logging/src/lib/logger.factory'; // β Wrong
+
+// Verify console filters in browser DevTools
+// Ensure Info, Debug, Warnings are enabled
+```
+
+## Common Error Messages
+
+| Error | Cause | Fix |
+|-------|-------|-----|
+| `NullInjectorError: LoggingService` | Missing config | Add `provideLogging()` |
+| `Type 'X' not assignable` | Wrong context type | Use object or function |
+| `Cannot read property 'X'` | Undefined property | Use optional chaining |
+| `Circular dependency` | Service injection | Use `logger()` factory |
+| Stack overflow | Infinite loop in context | Don't call logger in context |
diff --git a/.claude/skills/tailwind/SKILL.md b/.claude/skills/tailwind/SKILL.md
index 53ec3206a..8c302677e 100644
--- a/.claude/skills/tailwind/SKILL.md
+++ b/.claude/skills/tailwind/SKILL.md
@@ -9,29 +9,6 @@ description: This skill should be used when working with Tailwind CSS styling in
Assist with applying the ISA-specific Tailwind CSS design system throughout the ISA-Frontend Angular monorepo. This skill provides comprehensive knowledge of custom utilities, color palettes, typography classes, button variants, and layout patterns specific to this project.
-## When to Use This Skill
-
-Invoke this skill when:
-- **After** checking `libs/ui/**` for existing components (always check first!)
-- Styling layout and spacing for components
-- Choosing appropriate color values for custom elements
-- Applying typography classes to text content
-- Determining spacing, layout, or responsive breakpoints
-- Customizing or extending existing UI components
-- Ensuring design system consistency
-- Questions about which Tailwind utility classes are available
-
-**Important**: This skill provides Tailwind utilities. Always prefer using components from `@isa/ui/*` libraries before applying custom Tailwind styles.
-
-**Works together with:**
-- **[template-standards](../template-standards/SKILL.md)** - Angular template syntax, E2E testing attributes, and ARIA accessibility
-- **[logging](../logging/SKILL.md)** - MANDATORY logging in all Angular files
-
-When building Angular components, these skills work together:
-1. Use **template-standards** for Angular syntax, `data-*`, and ARIA attributes
-2. Use **tailwind** (this skill) for styling with the ISA design system
-3. Use **logging** for all component/service logging
-
## Core Design System Principles
### 0. Component Libraries First (Most Important)
diff --git a/.claude/skills/template-standards/SKILL.md b/.claude/skills/template-standards/SKILL.md
index 2309e2b2e..61bb2492f 100644
--- a/.claude/skills/template-standards/SKILL.md
+++ b/.claude/skills/template-standards/SKILL.md
@@ -7,20 +7,6 @@ description: This skill should be used when writing or reviewing Angular compone
Comprehensive guide for Angular templates covering modern syntax, E2E testing attributes, and ARIA accessibility.
-## When to Use
-
-- Creating or reviewing component templates
-- Refactoring legacy `*ngIf/*ngFor/*ngSwitch` to modern syntax
-- Implementing `@defer` lazy loading
-- Designing reusable components with `ng-content`
-- Adding E2E testing attributes for automated tests
-- Ensuring WCAG accessibility compliance
-- Template performance optimization
-
-**Related Skills:**
-- **[tailwind](../tailwind/SKILL.md)** - ISA design system styling (colors, typography, spacing, layout)
-- **[logging](../logging/SKILL.md)** - MANDATORY logging in all Angular files using `@isa/core/logging`
-
## Overview
This skill combines three essential aspects of Angular template development:
@@ -31,7 +17,9 @@ This skill combines three essential aspects of Angular template development:
**Every interactive element MUST include both E2E and ARIA attributes.**
----
+**Related Skills:**
+- **tailwind** - ISA design system styling (colors, typography, spacing, layout)
+- **logging** - MANDATORY logging in all Angular files using `@isa/core/logging`
## Part 1: Angular Template Syntax
@@ -81,6 +69,8 @@ This skill combines three essential aspects of Angular template development:
}
```
+**See [control-flow-reference.md](references/control-flow-reference.md) for advanced patterns including nested loops, complex conditions, and filter strategies.**
+
### @defer Lazy Loading
#### Basic Usage
@@ -97,7 +87,7 @@ This skill combines three essential aspects of Angular template development:
}
```
-#### Triggers
+#### Common Triggers
| Trigger | Use Case |
|---------|----------|
@@ -111,18 +101,15 @@ This skill combines three essential aspects of Angular template development:
**Multiple triggers:** `@defer (on interaction; on timer(5s))`
**Prefetching:** `@defer (on interaction; prefetch on idle)`
-#### Requirements
+#### Critical Requirements
- Components **MUST be standalone**
- No `@ViewChild`/`@ContentChild` references
- Reserve space in `@placeholder` to prevent layout shift
+- Never defer above-the-fold content (harms LCP)
+- Avoid `immediate`/`timer` during initial render (harms TTI)
-#### Best Practices
-
-- β
Defer below-the-fold content
-- β Never defer above-the-fold (harms LCP)
-- β Avoid `immediate`/`timer` during initial render (harms TTI)
-- Test with network throttling
+**See [defer-patterns.md](references/defer-patterns.md) for performance optimization, Core Web Vitals impact, bundle size reduction strategies, and real-world examples.**
### Content Projection
@@ -156,12 +143,9 @@ This skill combines three essential aspects of Angular template development:
```
-**Fallback content:** `Default Title`
-**Aliasing:** `Title
`
+**CRITICAL Constraint:** `ng-content` **always instantiates** (even if hidden). For conditional projection, use `ng-template` + `NgTemplateOutlet`.
-#### CRITICAL Constraint
-
-`ng-content` **always instantiates** (even if hidden). For conditional projection, use `ng-template` + `NgTemplateOutlet`.
+**See [projection-patterns.md](references/projection-patterns.md) for conditional projection, template-based projection, querying projected content, and modal/form field examples.**
### Template References
@@ -193,6 +177,8 @@ Groups elements without DOM footprint:
```
+**See [template-reference.md](references/template-reference.md) for programmatic rendering, ViewContainerRef patterns, context scoping, and common pitfalls.**
+
### Variables
#### @let (Angular 18.1+)
@@ -226,16 +212,14 @@ Groups elements without DOM footprint:
**Class:** `[class.active]="isActive()"` or `[class]="{active: isActive()}"`
**Style:** `[style.width.px]="width()"` or `[style]="{color: textColor()}"`
----
-
## Part 2: E2E Testing Attributes
### Purpose
Enable automated end-to-end testing by providing stable selectors for QA automation:
-- **`data-what`**: Semantic description of element's purpose
-- **`data-which`**: Unique identifier for specific instances
-- **`data-*`**: Additional contextual information
+- **`data-what`**: Semantic description of element's purpose (e.g., `submit-button`, `email-input`)
+- **`data-which`**: Unique identifier for specific instances (e.g., `registration-form`, `customer-123`)
+- **`data-*`**: Additional contextual information (e.g., `data-status="active"`)
### Naming Conventions
@@ -259,9 +243,8 @@ Enable automated end-to-end testing by providing stable selectors for QA automat
3. β
Ensure `data-which` is unique within the view
4. β
Use Angular binding for dynamic values: `[attr.data-*]`
5. β
Avoid including sensitive data in attributes
-6. β
Document complex attribute patterns in template comments
----
+**See [e2e-attributes.md](references/e2e-attributes.md) for complete patterns by element type (buttons, inputs, links, lists, tables, dialogs), dynamic attribute bindings, testing integration examples, and validation strategies.**
## Part 3: ARIA Accessibility Attributes
@@ -290,7 +273,7 @@ Ensure web applications are accessible to all users, including those using assis
6. β
Keep ARIA attributes in sync with visual states
7. β
Test with screen readers (NVDA, JAWS, VoiceOver)
----
+**See [aria-attributes.md](references/aria-attributes.md) for comprehensive role reference, property and state attributes, live regions, keyboard navigation patterns, WCAG compliance requirements, and testing strategies.**
## Part 4: Combined Examples
@@ -342,18 +325,6 @@ Ensure web applications are accessible to all users, including those using assis
}
```
-### Link with All Attributes
-
-```html
-
- View Order #{{ orderNumber }}
-
-```
-
### Dialog with All Attributes
```html
@@ -387,109 +358,7 @@ Ensure web applications are accessible to all users, including those using assis
```
-### Complete Form Example
-
-```html
-
-```
-
-### Conditional Rendering with Attributes
-
-```typescript
-@if (isLoading()) {
-
-
-
-} @else if (error()) {
-
- {{ error() }}
-
-} @else {
-
-
{{ user().name }}
-
-
-}
-```
-
----
+**See [combined-patterns.md](references/combined-patterns.md) for complete form examples, product listings, shopping carts, modal dialogs, navigation patterns, data tables, search interfaces, notifications, and multi-step forms with all attributes properly applied.**
## Validation Checklist
@@ -501,7 +370,6 @@ Before considering template complete:
- [ ] Complex expressions moved to `computed()` in component
- [ ] @defer used for below-the-fold or heavy components
- [ ] Content projection using ng-content with proper selectors (if applicable)
-- [ ] Template references using # for local variables
- [ ] Using @let for template-scoped variables (Angular 18.1+)
### E2E Attributes
@@ -510,7 +378,6 @@ Before considering template complete:
- [ ] All links have `data-what` and `data-which`
- [ ] Dynamic lists use `[attr.data-*]` bindings with unique identifiers
- [ ] No duplicate `data-which` values within the same view
-- [ ] Additional `data-*` attributes for contextual information (if needed)
### ARIA Accessibility
- [ ] All interactive elements have appropriate ARIA labels
@@ -520,16 +387,13 @@ Before considering template complete:
- [ ] Dialogs have `role="dialog"`, `aria-modal`, and label relationships
- [ ] Dynamic state changes reflected in ARIA attributes
- [ ] Keyboard accessibility (tabindex, enter/space handlers where needed)
-- [ ] Screen reader testing completed (if applicable)
### Combined Standards
- [ ] Every interactive element has BOTH E2E and ARIA attributes
-- [ ] Attributes organized logically in template (Angular directives β data-* β aria-*)
+- [ ] Attributes organized logically (Angular directives β data-* β aria-*)
- [ ] Dynamic bindings use `[attr.*]` syntax correctly
- [ ] No accessibility violations (semantic HTML preferred over ARIA)
----
-
## Migration Guide
### From Legacy Angular Syntax
@@ -549,31 +413,27 @@ Before considering template complete:
3. **Add ARIA attributes**: appropriate role, label, and state attributes
4. **Test**: verify selectors work in E2E tests, validate with screen readers
----
-
## Reference Files
For detailed examples and advanced patterns, see:
### Angular Syntax References
-- `references/control-flow-reference.md` - @if/@for/@switch patterns
-- `references/defer-patterns.md` - Lazy loading strategies
-- `references/projection-patterns.md` - Advanced ng-content
-- `references/template-reference.md` - ng-template/ng-container
+- `references/control-flow-reference.md` - Advanced @if/@for/@switch patterns, nested loops, filtering
+- `references/defer-patterns.md` - Lazy loading strategies, Core Web Vitals, performance optimization
+- `references/projection-patterns.md` - Advanced ng-content, conditional projection, template-based patterns
+- `references/template-reference.md` - ng-template/ng-container, programmatic rendering, ViewContainerRef
### E2E Testing References
-- `references/e2e-attributes.md` - Complete E2E attribute patterns and conventions
+- `references/e2e-attributes.md` - Complete E2E attribute patterns, naming conventions, testing integration
### ARIA Accessibility References
-- `references/aria-attributes.md` - Comprehensive ARIA guidance and WCAG compliance
+- `references/aria-attributes.md` - Comprehensive ARIA guidance, roles, properties, states, WCAG compliance
### Combined References
-- `references/combined-patterns.md` - Real-world examples with Angular + E2E + ARIA
+- `references/combined-patterns.md` - Real-world examples with Angular + E2E + ARIA integrated
Search with: `grep -r "pattern" references/`
----
-
## Quick Reference Summary
**Every interactive element needs:**
diff --git a/.claude/skills/test-migration/SKILL.md b/.claude/skills/test-migration/SKILL.md
index 8b28fbedf..7b9cdfa17 100644
--- a/.claude/skills/test-migration/SKILL.md
+++ b/.claude/skills/test-migration/SKILL.md
@@ -1,192 +1,192 @@
----
-name: test-migration
-description: Reference patterns for Jest to Vitest test migration. Contains syntax mappings for jestβvi, SpectatorβAngular Testing Library, and common matcher conversions. Auto-loaded by migration-specialist agent.
----
-
-# Jest to Vitest Migration Patterns
-
-Quick reference for test framework migration syntax.
-
-## Import Changes
-
-```typescript
-// REMOVE
-import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
-
-// ADD
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { describe, it, expect, beforeEach, vi } from 'vitest';
-```
-
-## Mock Migration
-
-| Jest | Vitest |
-|------|--------|
-| `jest.fn()` | `vi.fn()` |
-| `jest.spyOn(obj, 'method')` | `vi.spyOn(obj, 'method')` |
-| `jest.mock('module')` | `vi.mock('module')` |
-| `jest.useFakeTimers()` | `vi.useFakeTimers()` |
-| `jest.advanceTimersByTime(ms)` | `vi.advanceTimersByTime(ms)` |
-| `jest.useRealTimers()` | `vi.useRealTimers()` |
-| `jest.clearAllMocks()` | `vi.clearAllMocks()` |
-| `jest.resetAllMocks()` | `vi.resetAllMocks()` |
-
-## Spectator β TestBed
-
-### Component Testing
-
-```typescript
-// OLD (Spectator)
-const createComponent = createComponentFactory({
- component: MyComponent,
- imports: [CommonModule],
- mocks: [MyService]
-});
-let spectator: Spectator
;
-beforeEach(() => spectator = createComponent());
-
-// NEW (TestBed)
-let fixture: ComponentFixture;
-let component: MyComponent;
-beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [MyComponent],
- providers: [{ provide: MyService, useValue: mockService }]
- }).compileComponents();
- fixture = TestBed.createComponent(MyComponent);
- component = fixture.componentInstance;
-});
-```
-
-### Service Testing
-
-```typescript
-// OLD (Spectator)
-const createService = createServiceFactory({
- service: MyService,
- mocks: [HttpClient]
-});
-let spectator: SpectatorService;
-beforeEach(() => spectator = createService());
-
-// NEW (TestBed)
-let service: MyService;
-beforeEach(() => {
- TestBed.configureTestingModule({
- providers: [
- MyService,
- { provide: HttpClient, useValue: mockHttp }
- ]
- });
- service = TestBed.inject(MyService);
-});
-```
-
-## Query Selectors
-
-| Spectator | Angular/Native |
-|-----------|---------------|
-| `spectator.query('.class')` | `fixture.nativeElement.querySelector('.class')` |
-| `spectator.queryAll('.class')` | `fixture.nativeElement.querySelectorAll('.class')` |
-| `spectator.query('button')` | `fixture.nativeElement.querySelector('button')` |
-
-## Events
-
-| Spectator | Native |
-|-----------|--------|
-| `spectator.click(element)` | `element.click()` + `fixture.detectChanges()` |
-| `spectator.typeInElement(value, el)` | `el.value = value; el.dispatchEvent(new Event('input'))` |
-| `spectator.blur(element)` | `element.dispatchEvent(new Event('blur'))` |
-
-## Matchers
-
-| Spectator | Standard |
-|-----------|----------|
-| `toHaveText('text')` | `expect(el.textContent).toContain('text')` |
-| `toExist()` | `toBeTruthy()` |
-| `toBeVisible()` | Check `!el.hidden && el.offsetParent !== null` |
-| `toHaveClass('class')` | `expect(el.classList.contains('class')).toBe(true)` |
-
-## Async Patterns
-
-```typescript
-// OLD (callback)
-it('should emit', (done) => {
- service.data$.subscribe(val => {
- expect(val).toBe(expected);
- done();
- });
-});
-
-// NEW (async/await)
-import { firstValueFrom } from 'rxjs';
-it('should emit', async () => {
- const val = await firstValueFrom(service.data$);
- expect(val).toBe(expected);
-});
-```
-
-## Input/Output Testing
-
-```typescript
-// Setting inputs (Angular 17.3+)
-fixture.componentRef.setInput('title', 'Test');
-fixture.detectChanges();
-
-// Testing outputs
-const emitSpy = vi.fn();
-component.myOutput.subscribe(emitSpy);
-// trigger action...
-expect(emitSpy).toHaveBeenCalledWith(expectedValue);
-```
-
-## Configuration Files
-
-### vite.config.mts Template
-
-```typescript
-///
-import { defineConfig } from 'vite';
-import angular from '@analogjs/vite-plugin-angular';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
-
-export default
-// @ts-expect-error - Vitest reporter tuple types have complex inference issues
-defineConfig(() => ({
- root: __dirname,
- cacheDir: '../../../node_modules/.vite/libs/[path]', // Adjust depth!
- plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
- test: {
- watch: false,
- globals: true,
- environment: 'jsdom',
- include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- setupFiles: ['src/test-setup.ts'],
- reporters: [
- 'default',
- ['junit', { outputFile: '../../../testresults/junit-[name].xml' }],
- ],
- coverage: {
- reportsDirectory: '../../../coverage/libs/[path]',
- provider: 'v8' as const,
- reporter: ['text', 'cobertura'],
- },
- },
-}));
-```
-
-### test-setup.ts
-
-```typescript
-import '@analogjs/vitest-angular/setup-zone';
-```
-
-## Path Depth Reference
-
-| Library Location | Relative Path Prefix |
-|-----------------|---------------------|
-| `libs/feature/ui` | `../../` |
-| `libs/feature/data-access` | `../../` |
-| `libs/domain/feature/ui` | `../../../` |
-| `libs/domain/feature/data-access/store` | `../../../../` |
+---
+name: test-migration
+description: Reference patterns for Jest to Vitest test migration. Contains syntax mappings for jestβvi, SpectatorβAngular Testing Library, and common matcher conversions. Auto-loaded by migration-specialist agent.
+---
+
+# Jest to Vitest Migration Patterns
+
+Quick reference for test framework migration syntax.
+
+## Import Changes
+
+```typescript
+// REMOVE
+import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
+
+// ADD
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { describe, it, expect, beforeEach, vi } from 'vitest';
+```
+
+## Mock Migration
+
+| Jest | Vitest |
+|------|--------|
+| `jest.fn()` | `vi.fn()` |
+| `jest.spyOn(obj, 'method')` | `vi.spyOn(obj, 'method')` |
+| `jest.mock('module')` | `vi.mock('module')` |
+| `jest.useFakeTimers()` | `vi.useFakeTimers()` |
+| `jest.advanceTimersByTime(ms)` | `vi.advanceTimersByTime(ms)` |
+| `jest.useRealTimers()` | `vi.useRealTimers()` |
+| `jest.clearAllMocks()` | `vi.clearAllMocks()` |
+| `jest.resetAllMocks()` | `vi.resetAllMocks()` |
+
+## Spectator β TestBed
+
+### Component Testing
+
+```typescript
+// OLD (Spectator)
+const createComponent = createComponentFactory({
+ component: MyComponent,
+ imports: [CommonModule],
+ mocks: [MyService]
+});
+let spectator: Spectator;
+beforeEach(() => spectator = createComponent());
+
+// NEW (TestBed)
+let fixture: ComponentFixture;
+let component: MyComponent;
+beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MyComponent],
+ providers: [{ provide: MyService, useValue: mockService }]
+ }).compileComponents();
+ fixture = TestBed.createComponent(MyComponent);
+ component = fixture.componentInstance;
+});
+```
+
+### Service Testing
+
+```typescript
+// OLD (Spectator)
+const createService = createServiceFactory({
+ service: MyService,
+ mocks: [HttpClient]
+});
+let spectator: SpectatorService;
+beforeEach(() => spectator = createService());
+
+// NEW (TestBed)
+let service: MyService;
+beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ MyService,
+ { provide: HttpClient, useValue: mockHttp }
+ ]
+ });
+ service = TestBed.inject(MyService);
+});
+```
+
+## Query Selectors
+
+| Spectator | Angular/Native |
+|-----------|---------------|
+| `spectator.query('.class')` | `fixture.nativeElement.querySelector('.class')` |
+| `spectator.queryAll('.class')` | `fixture.nativeElement.querySelectorAll('.class')` |
+| `spectator.query('button')` | `fixture.nativeElement.querySelector('button')` |
+
+## Events
+
+| Spectator | Native |
+|-----------|--------|
+| `spectator.click(element)` | `element.click()` + `fixture.detectChanges()` |
+| `spectator.typeInElement(value, el)` | `el.value = value; el.dispatchEvent(new Event('input'))` |
+| `spectator.blur(element)` | `element.dispatchEvent(new Event('blur'))` |
+
+## Matchers
+
+| Spectator | Standard |
+|-----------|----------|
+| `toHaveText('text')` | `expect(el.textContent).toContain('text')` |
+| `toExist()` | `toBeTruthy()` |
+| `toBeVisible()` | Check `!el.hidden && el.offsetParent !== null` |
+| `toHaveClass('class')` | `expect(el.classList.contains('class')).toBe(true)` |
+
+## Async Patterns
+
+```typescript
+// OLD (callback)
+it('should emit', (done) => {
+ service.data$.subscribe(val => {
+ expect(val).toBe(expected);
+ done();
+ });
+});
+
+// NEW (async/await)
+import { firstValueFrom } from 'rxjs';
+it('should emit', async () => {
+ const val = await firstValueFrom(service.data$);
+ expect(val).toBe(expected);
+});
+```
+
+## Input/Output Testing
+
+```typescript
+// Setting inputs (Angular 17.3+)
+fixture.componentRef.setInput('title', 'Test');
+fixture.detectChanges();
+
+// Testing outputs
+const emitSpy = vi.fn();
+component.myOutput.subscribe(emitSpy);
+// trigger action...
+expect(emitSpy).toHaveBeenCalledWith(expectedValue);
+```
+
+## Configuration Files
+
+### vite.config.mts Template
+
+```typescript
+///
+import { defineConfig } from 'vite';
+import angular from '@analogjs/vite-plugin-angular';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
+
+export default
+// @ts-expect-error - Vitest reporter tuple types have complex inference issues
+defineConfig(() => ({
+ root: __dirname,
+ cacheDir: '../../../node_modules/.vite/libs/[path]', // Adjust depth!
+ plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
+ test: {
+ watch: false,
+ globals: true,
+ environment: 'jsdom',
+ include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ setupFiles: ['src/test-setup.ts'],
+ reporters: [
+ 'default',
+ ['junit', { outputFile: '../../../testresults/junit-[name].xml' }],
+ ],
+ coverage: {
+ reportsDirectory: '../../../coverage/libs/[path]',
+ provider: 'v8' as const,
+ reporter: ['text', 'cobertura'],
+ },
+ },
+}));
+```
+
+### test-setup.ts
+
+```typescript
+import '@analogjs/vitest-angular/setup-zone';
+```
+
+## Path Depth Reference
+
+| Library Location | Relative Path Prefix |
+|-----------------|---------------------|
+| `libs/feature/ui` | `../../` |
+| `libs/feature/data-access` | `../../` |
+| `libs/domain/feature/ui` | `../../../` |
+| `libs/domain/feature/data-access/store` | `../../../../` |