Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
735
.knowledge/dss-coding-standards.json
Normal file
735
.knowledge/dss-coding-standards.json
Normal file
@@ -0,0 +1,735 @@
|
||||
{
|
||||
"$schema": "dss-knowledge-v1",
|
||||
"type": "coding_standards",
|
||||
"version": "1.0.0",
|
||||
"last_updated": "2025-12-08",
|
||||
"description": "Immutable coding best practices for all DSS code. These standards are enforced via pre-commit hooks and guide all code contributions.",
|
||||
|
||||
"web_components": {
|
||||
"principle": "All UI components MUST be Web Components using Custom Elements API",
|
||||
"rules": [
|
||||
{
|
||||
"id": "WC-001",
|
||||
"rule": "Shadow DOM Required",
|
||||
"requirement": "MUST use attachShadow({ mode: 'open' }) for all components",
|
||||
"exceptions": "None - this is non-negotiable for style encapsulation",
|
||||
"rationale": "Prevents global CSS pollution and ensures component isolation",
|
||||
"enforcement": "Pre-commit hook fails if component extends HTMLElement without Shadow DOM"
|
||||
},
|
||||
{
|
||||
"id": "WC-002",
|
||||
"rule": "Lifecycle Management",
|
||||
"requirement": "MUST implement connectedCallback, disconnectedCallback, and attributeChangedCallback where appropriate",
|
||||
"pattern": "Always clean up event listeners and subscriptions in disconnectedCallback",
|
||||
"example": "disconnectedCallback() { if (this.unsubscribe) this.unsubscribe(); if (this.abortController) this.abortController.abort(); }"
|
||||
},
|
||||
{
|
||||
"id": "WC-003",
|
||||
"rule": "Observable Attributes",
|
||||
"requirement": "Define observedAttributes static getter for reactive attributes",
|
||||
"pattern": "static get observedAttributes() { return ['title', 'value', 'color']; }",
|
||||
"rationale": "Enables reactive attribute changes without manual DOM manipulation"
|
||||
},
|
||||
{
|
||||
"id": "WC-004",
|
||||
"rule": "Component Registration",
|
||||
"requirement": "Define custom element in component file using customElements.define()",
|
||||
"pattern": "customElements.define('ds-component-name', ComponentClass);",
|
||||
"naming": "All DSS components prefixed with 'ds-' to avoid conflicts"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"style_management": {
|
||||
"principle": "Zero inline styles policy - all styles in Shadow DOM or external stylesheets",
|
||||
"rules": [
|
||||
{
|
||||
"id": "STYLE-001",
|
||||
"rule": "NO Inline Styles",
|
||||
"requirement": "FORBIDDEN: style=\"...\" attributes in templates",
|
||||
"exceptions": "ONLY dynamic computed values (e.g., transform: translateX(${x}px), width: ${percent}%)",
|
||||
"enforcement": "Pre-commit hook fails if >10 inline styles detected",
|
||||
"rationale": "Maintainability, separation of concerns, style encapsulation"
|
||||
},
|
||||
{
|
||||
"id": "STYLE-002",
|
||||
"rule": "Shadow DOM Styles",
|
||||
"requirement": "All component styles in <style> block within shadowRoot.innerHTML",
|
||||
"pattern": "this.shadowRoot.innerHTML = `<style>:host { display: block; } .card { padding: 16px; }</style><div class=\"card\">...</div>`;",
|
||||
"best_practice": "Define styles at top of template for clarity"
|
||||
},
|
||||
{
|
||||
"id": "STYLE-003",
|
||||
"rule": "VSCode Theme Tokens",
|
||||
"requirement": "MUST use CSS custom properties from VSCode theme",
|
||||
"required_variables": [
|
||||
"--vscode-foreground",
|
||||
"--vscode-background",
|
||||
"--vscode-sidebar-background",
|
||||
"--vscode-button-background",
|
||||
"--vscode-button-foreground",
|
||||
"--vscode-button-secondaryBackground",
|
||||
"--vscode-button-secondaryForeground",
|
||||
"--vscode-focusBorder",
|
||||
"--vscode-list-hoverBackground",
|
||||
"--vscode-list-activeSelectionBackground",
|
||||
"--vscode-list-activeSelectionForeground",
|
||||
"--vscode-descriptionForeground",
|
||||
"--vscode-widget-border",
|
||||
"--vscode-errorForeground"
|
||||
],
|
||||
"rationale": "Ensures consistent theming with VSCode dark/light mode"
|
||||
},
|
||||
{
|
||||
"id": "STYLE-004",
|
||||
"rule": "Constructable Stylesheets",
|
||||
"requirement": "Use for shared styles across multiple components",
|
||||
"pattern": "const sheet = new CSSStyleSheet(); sheet.replaceSync(css); shadowRoot.adoptedStyleSheets = [sheet];",
|
||||
"use_cases": ["Common button styles", "Typography system", "Layout utilities"]
|
||||
},
|
||||
{
|
||||
"id": "STYLE-005",
|
||||
"rule": "CSS Hover Effects",
|
||||
"requirement": "Use :hover pseudo-class, NOT onmouseover/onmouseout",
|
||||
"correct": ".card:hover { background: var(--vscode-list-hoverBackground); }",
|
||||
"forbidden": "onmouseover=\"this.style.background='...'\" onmouseout=\"this.style.background=''\"",
|
||||
"rationale": "Performance, maintainability, separation of concerns"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"event_handling": {
|
||||
"principle": "Event delegation pattern - NO inline event handlers",
|
||||
"rules": [
|
||||
{
|
||||
"id": "EVENT-001",
|
||||
"rule": "NO Inline Events",
|
||||
"requirement": "FORBIDDEN: onclick, onmouseover, onmouseout, onkeydown, onkeyup, etc. in HTML",
|
||||
"enforcement": "Pre-commit hook fails if ANY inline event handlers detected",
|
||||
"rationale": "Security (CSP compliance), maintainability, testability, separation of concerns"
|
||||
},
|
||||
{
|
||||
"id": "EVENT-002",
|
||||
"rule": "Event Delegation Pattern",
|
||||
"requirement": "Single delegated listener per component using data-action attributes",
|
||||
"pattern": "this.shadowRoot.addEventListener('click', (e) => { const action = e.target.closest('[data-action]')?.dataset.action; if (action && this[`handle${action}`]) { this[`handle${action}`](e); } });",
|
||||
"html_example": "<button data-action=\"save\" type=\"button\">Save</button>",
|
||||
"js_example": "handleSave(e) { /* implementation */ }"
|
||||
},
|
||||
{
|
||||
"id": "EVENT-003",
|
||||
"rule": "Custom Events for Communication",
|
||||
"requirement": "Component communication via CustomEvent with composed: true",
|
||||
"pattern": "this.dispatchEvent(new CustomEvent('ds-action', { detail: { action, data }, bubbles: true, composed: true }));",
|
||||
"rationale": "composed: true allows events to bubble out of Shadow DOM boundaries"
|
||||
},
|
||||
{
|
||||
"id": "EVENT-004",
|
||||
"rule": "Event Listener Cleanup",
|
||||
"requirement": "Always remove event listeners in disconnectedCallback",
|
||||
"pattern": "this.abortController = new AbortController(); addEventListener('click', handler, { signal: this.abortController.signal }); // disconnectedCallback: this.abortController.abort();",
|
||||
"alternative": "Store handlers as methods and call removeEventListener explicitly"
|
||||
},
|
||||
{
|
||||
"id": "EVENT-005",
|
||||
"rule": "Keyboard Navigation",
|
||||
"requirement": "Support Tab, Enter, Escape, Arrow keys for interactive components",
|
||||
"pattern": "this.shadowRoot.addEventListener('keydown', (e) => { if (e.key === 'Escape') this.close(); if (e.key === 'Enter') this.submit(); });",
|
||||
"rationale": "Accessibility requirement for keyboard-only users"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"accessibility": {
|
||||
"principle": "WCAG 2.1 Level AA compliance minimum",
|
||||
"rules": [
|
||||
{
|
||||
"id": "A11Y-001",
|
||||
"rule": "Semantic HTML",
|
||||
"requirement": "MUST use semantic elements: <button>, <nav>, <main>, <aside>, <header>, <footer>, <article>, <section>",
|
||||
"forbidden": "<div onclick=\"...\"> for interactive elements, <div class=\"button\">",
|
||||
"correct": "<button type=\"button\">Click me</button>",
|
||||
"rationale": "Screen readers rely on semantic HTML for navigation and interaction"
|
||||
},
|
||||
{
|
||||
"id": "A11Y-002",
|
||||
"rule": "Button Type Attribute",
|
||||
"requirement": "ALL <button> elements MUST have type=\"button\" (or type=\"submit\" in forms)",
|
||||
"pattern": "<button type=\"button\">Action</button>",
|
||||
"rationale": "Prevents accidental form submission in HTML forms",
|
||||
"enforcement": "Pre-commit hook warns about buttons without type attribute"
|
||||
},
|
||||
{
|
||||
"id": "A11Y-003",
|
||||
"rule": "ARIA Attributes",
|
||||
"requirement": "Use when semantic HTML insufficient",
|
||||
"required_patterns": [
|
||||
"aria-label for icon-only buttons",
|
||||
"aria-labelledby for complex widgets",
|
||||
"aria-describedby for additional context",
|
||||
"role for custom interactive elements"
|
||||
],
|
||||
"examples": [
|
||||
"<button aria-label=\"Close dialog\" type=\"button\">×</button>",
|
||||
"<div role=\"group\" aria-labelledby=\"tools-heading\">...</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A11Y-004",
|
||||
"rule": "Focus Management",
|
||||
"requirement": "Keyboard navigation support for all interactive elements",
|
||||
"css_pattern": ".btn:focus-visible { outline: 2px solid var(--vscode-focusBorder); outline-offset: 2px; }",
|
||||
"js_requirements": [
|
||||
"trapFocus() for modals",
|
||||
"restoreFocus() on close",
|
||||
"Skip links for main content",
|
||||
"Focus first invalid field on validation error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A11Y-005",
|
||||
"rule": "Color Contrast",
|
||||
"requirement": "Minimum 4.5:1 for normal text, 3:1 for large text",
|
||||
"tool": "Use VSCode theme tokens which meet contrast requirements",
|
||||
"testing": "Chrome DevTools Lighthouse audit"
|
||||
},
|
||||
{
|
||||
"id": "A11Y-006",
|
||||
"rule": "Screen Reader Testing",
|
||||
"requirement": "Test all components with NVDA/JAWS (Windows) or VoiceOver (Mac)",
|
||||
"checklist": [
|
||||
"Navigation order makes sense",
|
||||
"All buttons have clear labels",
|
||||
"Error messages announced",
|
||||
"Status updates announced with aria-live"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"state_management": {
|
||||
"principle": "Centralized state with reactive updates",
|
||||
"rules": [
|
||||
{
|
||||
"id": "STATE-001",
|
||||
"rule": "Context Store for Global State",
|
||||
"requirement": "Use contextStore for application-wide state (projectId, teamId, userId)",
|
||||
"pattern": "this.unsubscribe = contextStore.subscribeToKey('projectId', (newValue) => { this.projectId = newValue; this.render(); });",
|
||||
"path": "admin-ui/js/stores/context-store.js"
|
||||
},
|
||||
{
|
||||
"id": "STATE-002",
|
||||
"rule": "Component Local State",
|
||||
"requirement": "Component-specific state in this.state object",
|
||||
"pattern": "this.state = { isLoading: false, data: null, error: null }; setState(updates) { Object.assign(this.state, updates); this.render(); }",
|
||||
"best_practice": "Initialize state in constructor"
|
||||
},
|
||||
{
|
||||
"id": "STATE-003",
|
||||
"rule": "NO Direct DOM Manipulation",
|
||||
"requirement": "State changes trigger re-renders, not direct DOM updates",
|
||||
"forbidden": "document.getElementById('foo').textContent = 'bar'; element.style.display = 'none';",
|
||||
"correct": "this.setState({ foo: 'bar', visible: false }); // render() handles DOM updates",
|
||||
"rationale": "Maintains single source of truth, prevents state/view desync"
|
||||
},
|
||||
{
|
||||
"id": "STATE-004",
|
||||
"rule": "Subscription Cleanup",
|
||||
"requirement": "Always unsubscribe in disconnectedCallback",
|
||||
"pattern": "connectedCallback() { this.unsubscribe = store.subscribe(...); } disconnectedCallback() { if (this.unsubscribe) this.unsubscribe(); }",
|
||||
"rationale": "Prevents memory leaks from orphaned subscriptions"
|
||||
},
|
||||
{
|
||||
"id": "STATE-005",
|
||||
"rule": "Immutable State Updates",
|
||||
"requirement": "Create new state objects, don't mutate existing",
|
||||
"correct": "this.state = { ...this.state, count: this.state.count + 1 };",
|
||||
"forbidden": "this.state.count++; // direct mutation",
|
||||
"rationale": "Enables change detection and debugging"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"code_organization": {
|
||||
"principle": "Single Responsibility, clear structure, modular design",
|
||||
"rules": [
|
||||
{
|
||||
"id": "ORG-001",
|
||||
"rule": "File Size Limit",
|
||||
"requirement": "Maximum 500 lines per file",
|
||||
"action": "If larger, split into multiple modules",
|
||||
"enforcement": "Quality check warns on files >500 lines",
|
||||
"current_violation": "app.js at 4,347 lines must be decomposed"
|
||||
},
|
||||
{
|
||||
"id": "ORG-002",
|
||||
"rule": "Directory Structure",
|
||||
"requirement": "Strict organization by type and purpose",
|
||||
"structure": {
|
||||
"admin-ui/js/components/": "Web components (*.js)",
|
||||
"admin-ui/js/components/layout/": "Layout components (shell, header, sidebar)",
|
||||
"admin-ui/js/components/metrics/": "Dashboard and metric components",
|
||||
"admin-ui/js/components/tools/": "Tool-specific components",
|
||||
"admin-ui/js/components/listings/": "List/table components",
|
||||
"admin-ui/js/stores/": "State management",
|
||||
"admin-ui/js/utils/": "Helper functions and utilities",
|
||||
"admin-ui/js/core/": "Core modules (router, messaging, workflows)",
|
||||
"admin-ui/js/workdesks/": "Team workdesk controllers",
|
||||
"admin-ui/js/config/": "Configuration files"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ORG-003",
|
||||
"rule": "Import Style",
|
||||
"requirement": "Explicit named imports, no wildcards",
|
||||
"correct": "import { hydrateComponent } from '../config/component-registry.js';",
|
||||
"forbidden": "import * as registry from '../config/component-registry.js';",
|
||||
"rationale": "Tree-shaking, explicit dependencies, better IDE support"
|
||||
},
|
||||
{
|
||||
"id": "ORG-004",
|
||||
"rule": "Export Style",
|
||||
"requirement": "Named exports for utilities, default export for components",
|
||||
"utility_pattern": "export function debounce(fn, ms) { ... } export function throttle(fn, ms) { ... }",
|
||||
"component_pattern": "export default class MyComponent extends HTMLElement { ... }",
|
||||
"rationale": "Convention clarity, supports tree-shaking"
|
||||
},
|
||||
{
|
||||
"id": "ORG-005",
|
||||
"rule": "Single Responsibility Principle",
|
||||
"requirement": "One component = one concern, one file = one primary export",
|
||||
"examples": [
|
||||
"ds-metric-card.js: Displays a single metric card",
|
||||
"ds-frontpage.js: Orchestrates dashboard layout",
|
||||
"context-store.js: Manages global application state"
|
||||
],
|
||||
"anti_pattern": "utility-functions.js with 50 unrelated functions"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"error_handling": {
|
||||
"principle": "Structured logging, user-friendly errors, graceful degradation",
|
||||
"rules": [
|
||||
{
|
||||
"id": "ERROR-001",
|
||||
"rule": "Centralized Logger",
|
||||
"requirement": "Use logger utility for all logging",
|
||||
"path": "admin-ui/js/utils/logger.js",
|
||||
"methods": [
|
||||
"logger.debug(msg, ...args): Development-only logging",
|
||||
"logger.info(msg, ...args): Informational messages",
|
||||
"logger.warn(msg, ...args): Warning conditions",
|
||||
"logger.error(msg, ...args): Error conditions"
|
||||
],
|
||||
"pattern": "import { logger } from '../utils/logger.js'; logger.info('[ComponentName] Action completed', { data });"
|
||||
},
|
||||
{
|
||||
"id": "ERROR-002",
|
||||
"rule": "NO console.log in Production",
|
||||
"requirement": "Replace all console.log/warn/error with logger methods",
|
||||
"enforcement": "Pre-commit hook warns if >10 console statements",
|
||||
"exceptions": "Core initialization logging in app.js only",
|
||||
"migration": "console.log('foo') → logger.debug('foo')"
|
||||
},
|
||||
{
|
||||
"id": "ERROR-003",
|
||||
"rule": "User-Friendly Error Messages",
|
||||
"requirement": "Error messages must be actionable and clear",
|
||||
"good": "Failed to load projects. Please check your internet connection and try again.",
|
||||
"bad": "Error: HTTP 500", "TypeError: Cannot read property 'id' of undefined",
|
||||
"pattern": "Show what failed + why it might have failed + what user can do"
|
||||
},
|
||||
{
|
||||
"id": "ERROR-004",
|
||||
"rule": "Error Taxonomy",
|
||||
"requirement": "Use structured error codes from messaging.js",
|
||||
"codes": {
|
||||
"E1xxx": "User errors (invalid input, forbidden actions)",
|
||||
"E2xxx": "Validation errors (missing fields, invalid formats)",
|
||||
"E3xxx": "API errors (request failed, timeout, unauthorized)",
|
||||
"E4xxx": "System errors (unexpected, network, storage)",
|
||||
"E5xxx": "Integration errors (Figma, external APIs)",
|
||||
"S1xxx": "Success codes"
|
||||
},
|
||||
"usage": "notifyError('E3001', 'Failed to fetch projects', error);"
|
||||
},
|
||||
{
|
||||
"id": "ERROR-005",
|
||||
"rule": "Graceful Degradation",
|
||||
"requirement": "Components must handle missing/invalid data gracefully",
|
||||
"patterns": [
|
||||
"if (!data) return this.renderEmptyState();",
|
||||
"if (error) return this.renderError(error);",
|
||||
"const items = data?.items ?? [];"
|
||||
],
|
||||
"anti_pattern": "Assuming data exists and causing cascading failures"
|
||||
},
|
||||
{
|
||||
"id": "ERROR-006",
|
||||
"rule": "Try-Catch for Async Operations",
|
||||
"requirement": "Wrap all async operations in try-catch",
|
||||
"pattern": "async loadData() { try { const data = await fetch(...); } catch (error) { logger.error('[Component] Failed to load', error); this.handleError(error); } }",
|
||||
"rationale": "Prevents unhandled promise rejections"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"performance": {
|
||||
"principle": "Optimize for user experience, measure and improve",
|
||||
"rules": [
|
||||
{
|
||||
"id": "PERF-001",
|
||||
"rule": "Lazy Loading",
|
||||
"requirement": "Non-critical components loaded on-demand via hydrateComponent()",
|
||||
"pattern": "await hydrateComponent('ds-screenshot-gallery', container);",
|
||||
"benefits": "Faster initial load, reduced bundle size",
|
||||
"applies_to": ["Tool components", "Heavy visualizations", "Rarely-used features"]
|
||||
},
|
||||
{
|
||||
"id": "PERF-002",
|
||||
"rule": "Virtual Scrolling",
|
||||
"requirement": "Use for lists >100 items",
|
||||
"libraries": ["lit-virtualizer", "virtual-scroller"],
|
||||
"pattern": "import { VirtualScroller } from 'virtual-scroller'; new VirtualScroller(container, { items, renderItem });",
|
||||
"applies_to": ["Token lists", "Component catalogs", "Log viewers"]
|
||||
},
|
||||
{
|
||||
"id": "PERF-003",
|
||||
"rule": "Debouncing and Throttling",
|
||||
"requirement": "Debounce search inputs, throttle scroll/resize handlers",
|
||||
"debounce_pattern": "this.searchDebounced = debounce(() => this.performSearch(), 300);",
|
||||
"throttle_pattern": "this.onScrollThrottled = throttle(() => this.handleScroll(), 100);",
|
||||
"use_cases": {
|
||||
"debounce": "Search inputs, form validation, autosave",
|
||||
"throttle": "Scroll handlers, resize handlers, mousemove tracking"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "PERF-004",
|
||||
"rule": "Avoid Unnecessary Re-renders",
|
||||
"requirement": "Only re-render when state actually changes",
|
||||
"pattern": "attributeChangedCallback(name, oldValue, newValue) { if (oldValue === newValue) return; this.render(); }",
|
||||
"best_practice": "Compare old vs new state before rendering"
|
||||
},
|
||||
{
|
||||
"id": "PERF-005",
|
||||
"rule": "Bundle Size Monitoring",
|
||||
"requirement": "Monitor and optimize JavaScript bundle sizes",
|
||||
"targets": {
|
||||
"individual_component": "<50KB",
|
||||
"total_bundle": "<500KB",
|
||||
"critical_path": "<200KB"
|
||||
},
|
||||
"tools": ["webpack-bundle-analyzer", "source-map-explorer"]
|
||||
},
|
||||
{
|
||||
"id": "PERF-006",
|
||||
"rule": "Minimize DOM Operations",
|
||||
"requirement": "Batch DOM updates, use DocumentFragment for multiple inserts",
|
||||
"pattern": "const fragment = document.createDocumentFragment(); items.forEach(item => fragment.appendChild(createNode(item))); container.appendChild(fragment);",
|
||||
"anti_pattern": "items.forEach(item => container.appendChild(createNode(item))); // causes multiple reflows"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"security": {
|
||||
"principle": "Secure by default, defense in depth",
|
||||
"rules": [
|
||||
{
|
||||
"id": "SEC-001",
|
||||
"rule": "NO Hardcoded Secrets",
|
||||
"requirement": "FORBIDDEN: API keys, passwords, tokens, credentials in code",
|
||||
"enforcement": "Pre-commit hook fails if secret patterns detected (apiKey, api_key, password, secret, token)",
|
||||
"correct": "Use environment variables or secure configuration service",
|
||||
"rationale": "Prevents credential leaks in version control"
|
||||
},
|
||||
{
|
||||
"id": "SEC-002",
|
||||
"rule": "Input Sanitization",
|
||||
"requirement": "ALWAYS escape user input before rendering to DOM",
|
||||
"utility": "ComponentHelpers.escapeHtml(userInput)",
|
||||
"pattern": "<div>${ComponentHelpers.escapeHtml(project.name)}</div>",
|
||||
"forbidden": "<div>${project.name}</div> // XSS vulnerability",
|
||||
"rationale": "Prevents XSS attacks"
|
||||
},
|
||||
{
|
||||
"id": "SEC-003",
|
||||
"rule": "CSP Compliance",
|
||||
"requirement": "NO eval(), NO new Function(), NO inline scripts",
|
||||
"forbidden": [
|
||||
"eval(code)",
|
||||
"new Function('x', 'return x * 2')",
|
||||
"<script>alert(1)</script> in templates"
|
||||
],
|
||||
"rationale": "Content Security Policy compatibility, prevents code injection"
|
||||
},
|
||||
{
|
||||
"id": "SEC-004",
|
||||
"rule": "HTTPS Only",
|
||||
"requirement": "All external requests use HTTPS",
|
||||
"enforcement": "No http:// URLs in fetch() calls",
|
||||
"correct": "fetch('https://api.example.com/data')",
|
||||
"forbidden": "fetch('http://api.example.com/data')",
|
||||
"exceptions": "localhost development only"
|
||||
},
|
||||
{
|
||||
"id": "SEC-005",
|
||||
"rule": "Validate API Responses",
|
||||
"requirement": "Always validate structure and content of API responses",
|
||||
"pattern": "const data = await response.json(); if (!data || !Array.isArray(data.items)) throw new Error('Invalid response');",
|
||||
"rationale": "Prevents errors from malformed/malicious responses"
|
||||
},
|
||||
{
|
||||
"id": "SEC-006",
|
||||
"rule": "Authentication Token Handling",
|
||||
"requirement": "Store auth tokens securely, never in localStorage",
|
||||
"correct": "sessionStorage (better: httpOnly cookies)",
|
||||
"forbidden": "localStorage for sensitive tokens",
|
||||
"rationale": "localStorage accessible to XSS attacks"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"testing_and_quality": {
|
||||
"principle": "Automated quality gates, comprehensive testing",
|
||||
"rules": [
|
||||
{
|
||||
"id": "TEST-001",
|
||||
"rule": "Pre-commit Hooks",
|
||||
"requirement": "MUST pass all quality checks before commit",
|
||||
"script": "scripts/verify-quality.sh",
|
||||
"thresholds": {
|
||||
"inline_styles": "≤10 (exceptions for dynamic values only)",
|
||||
"inline_events": "0 (zero tolerance)",
|
||||
"console_statements": "≤10 (production code only)",
|
||||
"file_size": "≤100KB per file",
|
||||
"syntax_errors": "0 (zero tolerance)"
|
||||
},
|
||||
"bypass": "git commit --no-verify (not recommended, requires justification)"
|
||||
},
|
||||
{
|
||||
"id": "TEST-002",
|
||||
"rule": "Component Unit Tests",
|
||||
"requirement": "Unit tests for all components",
|
||||
"coverage": "Minimum 80% for critical paths",
|
||||
"framework": "Web Test Runner or Vitest",
|
||||
"test_cases": [
|
||||
"Component renders correctly",
|
||||
"Props/attributes update component",
|
||||
"Event handlers work",
|
||||
"Cleanup happens on disconnect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TEST-003",
|
||||
"rule": "Integration Tests",
|
||||
"requirement": "Test critical user flows end-to-end",
|
||||
"examples": [
|
||||
"Project creation workflow",
|
||||
"Token extraction from Figma",
|
||||
"Component audit process",
|
||||
"User authentication flow"
|
||||
],
|
||||
"tools": ["Playwright", "Cypress"]
|
||||
},
|
||||
{
|
||||
"id": "TEST-004",
|
||||
"rule": "Visual Regression Testing",
|
||||
"requirement": "Screenshot comparison for UI changes",
|
||||
"tools": ["Playwright screenshots", "Percy", "Chromatic"],
|
||||
"process": "Capture baseline → Make changes → Compare → Approve/reject",
|
||||
"applies_to": "All visible UI components"
|
||||
},
|
||||
{
|
||||
"id": "TEST-005",
|
||||
"rule": "Documentation",
|
||||
"requirement": "JSDoc comments for public APIs and complex logic",
|
||||
"pattern": "/**\n * Load project data from API\n * @param {string} projectId - The project identifier\n * @returns {Promise<Project>} The loaded project\n * @throws {Error} If project not found or network error\n */\nasync loadProject(projectId) { ... }",
|
||||
"applies_to": ["Public component methods", "Utility functions", "Store APIs"]
|
||||
},
|
||||
{
|
||||
"id": "TEST-006",
|
||||
"rule": "Code Review Checklist",
|
||||
"requirement": "All PRs reviewed against these standards",
|
||||
"checklist": [
|
||||
"✓ Shadow DOM used for all components",
|
||||
"✓ No inline styles or event handlers",
|
||||
"✓ Proper accessibility attributes",
|
||||
"✓ Event listeners cleaned up",
|
||||
"✓ User input sanitized",
|
||||
"✓ Error handling implemented",
|
||||
"✓ Tests added/updated"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"enforcement": {
|
||||
"description": "How these standards are enforced in the development workflow",
|
||||
"mechanisms": [
|
||||
{
|
||||
"type": "Pre-commit Hooks",
|
||||
"file": ".git/hooks/pre-commit",
|
||||
"action": "Runs scripts/verify-quality.sh automatically",
|
||||
"can_bypass": "git commit --no-verify (requires justification)"
|
||||
},
|
||||
{
|
||||
"type": "Quality Verification Script",
|
||||
"file": "scripts/verify-quality.sh",
|
||||
"checks": [
|
||||
"Inline event handlers detection",
|
||||
"Inline styles counting",
|
||||
"Missing ARIA attributes",
|
||||
"Console.log statements",
|
||||
"JavaScript syntax validation",
|
||||
"Hardcoded secrets detection",
|
||||
"Shadow DOM usage statistics",
|
||||
"File size warnings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Immutability Protection",
|
||||
"file": ".clauderc",
|
||||
"protection": "This file listed in protected_core_files",
|
||||
"requirement": "ALLOW_CORE_CHANGES=true to modify"
|
||||
},
|
||||
{
|
||||
"type": "AI Agent Instructions",
|
||||
"description": "AI assistants programmed to follow these standards",
|
||||
"behavior": [
|
||||
"Always use Shadow DOM for components",
|
||||
"Never generate inline event handlers",
|
||||
"Extract inline styles to style blocks",
|
||||
"Add proper accessibility attributes",
|
||||
"Use logger instead of console.log"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"migration_strategy": {
|
||||
"description": "How to migrate existing code to these standards",
|
||||
"phases": [
|
||||
{
|
||||
"phase": 1,
|
||||
"name": "Foundation Fixes",
|
||||
"duration": "Week 1",
|
||||
"priority": "Critical",
|
||||
"tasks": [
|
||||
"Refactor tool-templates.js to generate compliant HTML",
|
||||
"Create logger utility (admin-ui/js/utils/logger.js)",
|
||||
"Update verify-quality.sh thresholds",
|
||||
"Create migration guide with examples"
|
||||
]
|
||||
},
|
||||
{
|
||||
"phase": 2,
|
||||
"name": "Component Migration - Batch A",
|
||||
"duration": "Week 2",
|
||||
"priority": "High",
|
||||
"targets": [
|
||||
"Layout components (ds-project-selector, ds-shell, ds-header)",
|
||||
"Most violated: ds-screenshot-gallery, ds-network-monitor"
|
||||
],
|
||||
"pattern": "Add Shadow DOM → Extract styles → Replace inline events → Add ARIA"
|
||||
},
|
||||
{
|
||||
"phase": 3,
|
||||
"name": "Component Migration - Batch B",
|
||||
"duration": "Week 3",
|
||||
"priority": "High",
|
||||
"targets": [
|
||||
"Tool components (ds-activity-log, ds-test-results)",
|
||||
"Admin/listings (ds-project-list, ds-token-list)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"phase": 4,
|
||||
"name": "Monolith Decomposition",
|
||||
"duration": "Week 4",
|
||||
"priority": "Critical",
|
||||
"target": "app.js (4,347 lines → 7 modules)",
|
||||
"modules": [
|
||||
"app.js: Main orchestrator (<500 lines)",
|
||||
"router.js: Hash routing",
|
||||
"auth-manager.js: Authentication",
|
||||
"api-client.js: Fetch wrapper",
|
||||
"error-handler.js: Global errors",
|
||||
"state-manager.js: State coordination",
|
||||
"init.js: Initialization"
|
||||
]
|
||||
},
|
||||
{
|
||||
"phase": 5,
|
||||
"name": "Quality Enforcement",
|
||||
"duration": "Week 5",
|
||||
"priority": "Medium",
|
||||
"tasks": [
|
||||
"Update thresholds (inline styles ≤10, events = 0)",
|
||||
"Add ESLint/Stylelint rules",
|
||||
"CI/CD integration",
|
||||
"Developer training"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"reference_implementations": {
|
||||
"description": "Examples of components that follow these standards",
|
||||
"files": [
|
||||
{
|
||||
"file": "admin-ui/js/workdesks/base-workdesk.js",
|
||||
"demonstrates": [
|
||||
"Semantic HTML (buttons not divs)",
|
||||
"Event delegation pattern",
|
||||
"Extracted styles in style block",
|
||||
"ARIA attributes",
|
||||
"Focus management"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "admin-ui/js/components/metrics/ds-frontpage.js",
|
||||
"demonstrates": [
|
||||
"Shadow DOM implementation",
|
||||
"Zero inline styles",
|
||||
"Lifecycle management",
|
||||
"State subscription pattern",
|
||||
"Event cleanup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "admin-ui/js/components/metrics/ds-metric-card.js",
|
||||
"demonstrates": [
|
||||
"Observable attributes",
|
||||
"Shadow DOM encapsulation",
|
||||
"Reactive rendering",
|
||||
"Component composition"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"success_metrics": {
|
||||
"description": "Measurable goals for DSS code quality",
|
||||
"current_state": {
|
||||
"shadow_dom_adoption": "23% (12/53 components)",
|
||||
"inline_event_handlers": "20+",
|
||||
"inline_styles": "1,288",
|
||||
"console_statements": "100+",
|
||||
"largest_file": "app.js at 4,347 lines / 156KB"
|
||||
},
|
||||
"target_state": {
|
||||
"shadow_dom_adoption": "100% (53/53 components)",
|
||||
"inline_event_handlers": "0 (zero tolerance)",
|
||||
"inline_styles": "<10 (dynamic values only)",
|
||||
"console_statements": "<10 (core only)",
|
||||
"largest_file": "<500 lines per file"
|
||||
},
|
||||
"tracking": {
|
||||
"method": "Pre-commit hook statistics",
|
||||
"frequency": "Every commit",
|
||||
"reporting": "Monthly code quality dashboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user