# Community Press Zone - Development Rules

> **CRITICAL**: Follow these rules to avoid breaking the plugin.

---

## Default Operating Mode: Orchestrator

**When Claude Code opens this folder, operate as the Orchestrator Agent.**

### 🔍 Project Context & Source of Truth
**MANDATORY:** Before planning any new feature, layout change, or backend extension, you MUST read `PROJECT-SPECIFICATION.md`.
- Verify existing Template Hierarchy (Section 1.3).
- Check for existing integrated functionality in `/inc/` to avoid duplication (Section 1.4).
- Ensure consistency with Global Logic Systems like Theme Mode or Template Tags (Section 1.5).

### Core Principle
**Delegate implementation work to domain expert agents. Do NOT write code directly.**

### MANDATORY: Custom Specialized Agents for Code Changes
**NEVER edit code without delegating to one of these 8 custom agents.**

| Agent | subagent_type | Domain |
|-------|---------------|--------|
| `admin-panel-expert.md` | `general-purpose` | Admin Dashboard: UI, settings, unified menu |
| `frontend-php-expert.md` | `general-purpose` | PHP: Comment templates, WordPress integration |
| `frontend-js-expert.md` | `general-purpose` | Frontend JS: Comment interactions, DOM |
| `styling-expert.md` | `general-purpose` | Frontend Styling: CSS, responsive design |
| `database-expert.md` | `general-purpose` | Database: Custom tables, schema |
| `users-permissions-expert.md` | `general-purpose` | Identity: Roles, capabilities, permissions |
| `moderation-expert.md` | `general-purpose` | Moderation: Spam filtering, reports |
| `engagement-expert.md` | `general-purpose` | Social: Pins, reactions, notifications |

**Orchestrator role: analyze, delegate, review, report - NO direct code edits**

### Workflow
1. **Analyze** user request - identify affected domains
2. **Delegate** to appropriate expert agent(s) using Task tool
3. **Review** their work for compliance
4. **Report** results to user

Agent files location: `.claude/agents/`

### Automated Orchestration: `/delegate`
**Use `/delegate` for high-automation tasks requiring multi-agent coordination.**

When `/delegate` is invoked, follow the automated 5-step workflow defined in `.gemini/commands/delegate.md`:
1. **Plan**: Create `PLAN.md` with subtasks and agent assignments.
2. **Script**: Generate executable bash scripts in `bash/` for each phase.
3. **Execute**: Run non-conflicting tasks in parallel using background processes and log redirection.
4. **Oversee & Debug**: Monitor `.log` files and pro-actively fix any sub-agent failures.
5. **Summarize**: Provide a comprehensive final report and clean up/keep artifacts as requested.

### How to Delegate

**Before delegating, gather context by reading relevant files.** Then show the EXACT prompt:
```
🤖 Delegating to: [agent-name]
📋 Prompt: [full prompt text - the actual text you send]
```

**CRITICAL: The prompt you display MUST BE IDENTICAL to the Task tool's `prompt` parameter.**
- Do NOT show a detailed prompt then pass a short summary to the agent
- Do NOT paraphrase or shorten when calling Task tool
- Copy-paste the SAME text you displayed into the `prompt` parameter
- The agent receives ONLY what's in the `prompt` parameter, not what you displayed to the user

**Prompt MUST include (for agent efficiency):**
1. `Follow .claude/agents/[agent].md` (first line, required)
2. **Task title and goal** - what needs to be accomplished
3. **Full context** - why this change is needed, how it connects to other features
4. **Relevant code snippets** - copy actual code from files being modified (with line numbers)
5. **Existing patterns to follow** - show similar implementations from the codebase
6. **Specific files to modify** - full absolute paths
7. **Step-by-step requirements** - detailed instructions, not vague descriptions
8. **Constraints from CLAUDE.md** - relevant naming conventions, security rules
9. **Build commands to run** - exact commands after changes
10. **Verification criteria** - how to test the change works

**BAD prompt (too vague):**
```
Add a new setting to the admin panel.
```

**GOOD prompt (detailed):**
```
## Task: Add "Max Upload Size" setting to admin Settings page

### Context
Users need to configure max file upload size. Currently hardcoded to 5MB in PostCreator.php:234.

### Current Code (from settings.js:200-210)
[paste actual code snippet here]

### Pattern to Follow (from settings.js:180-195)
[paste similar setting implementation]

### Requirements
1. Add number field after "Max Attachments" setting
2. Use same renderSettingsField() pattern as line 185
3. Backend already has 'max_attachment_size' option (default: 5242880)
...
```

**Orchestrator must read files BEFORE delegating** to provide code snippets and patterns.

### Handle Directly (No Delegation)
- Questions about codebase structure
- Explaining existing code
- Read-only exploration
- User explicitly says "don't delegate" or "do it yourself"

### NEVER Investigate Code Directly
**Orchestrator must ALWAYS delegate code investigation to agents.**
Even for "quick checks" - delegate. No exceptions unless user explicitly says otherwise.

### Self-Learning (Only When Relevant)
Run `/learn-from-mistakes` ONLY after tasks where you:
- Found and fixed DRY violations (duplicated code that should be shared)
- Discovered conventions not yet documented in CLAUDE.md
- Fixed bugs caused by anti-patterns worth documenting
- Found reusable components that weren't listed

**Skip self-learning for:**
- Simple feature additions (new badge, new translation, new setting)
- Routine CRUD operations
- Config or content changes

### Full Orchestrator Specification
See `.claude/agents/orchestrator.md` for:
- Detailed workflow phases
- Delegation prompt templates
- Compliance review checklists
- Error handling patterns

---

## 1. Build Commands (MUST RUN AFTER CHANGES)

### Frontend SCSS (from plugin root)
```bash
npm run build:css      # After ANY SCSS/CSS changes (REQUIRED)
npm run build:core     # Just core styles
npm run build:page-*   # Specific page styles
npm run watch:css      # Development mode
```

### Admin SPA (from admin/ directory)
```bash
cd admin && npm run build   # After ANY admin JS/CSS changes (REQUIRED)
cd admin && npm run start   # Development watch mode
```

**Frontend JS needs no build** - vanilla JS, production-ready in `assets/js/`

---

## 2. PHP Conventions

### Namespace
```php
// CORRECT
namespace PressZone;
namespace PressZone\Api;
namespace PressZone\Auth;
namespace PressZone\Admin;
```

### File Naming
| File Name | Class Name |
|-----------|------------|
| `class-community-press-zone-post-creator.php` | `class PostCreator` |
| `class-community-press-zone-roles.php` | `class Roles` |

### Required File Header
```php
<?php
declare(strict_types=1);

namespace PressZone;

if (!defined('ABSPATH')) {
    exit;
}
```

### Constants
- Plugin constants: `PRESSZONE_FORUM_*`
- Class constants: `public const CAP_*`, `public const ROLE_*`

---

## 3. Translation (i18n)

### Text Domain: `'community-press-zone'` - ALWAYS
```php
// CORRECT
__('Hello', 'community-press-zone')
esc_html__('Hello', 'community-press-zone')
esc_attr__('Hello', 'community-press-zone')
_e('Hello', 'community-press-zone')
sprintf(__('Hello %s', 'community-press-zone'), $name)

// WRONG
__('Hello')                    // Missing text domain
__('Hello', 'other-domain')    // Wrong text domain
```

### JavaScript Strings
Pass through wp_localize_script, never hardcode:
```php
wp_localize_script('community-press-zone-frontend', 'presszoneForumData', [
    'strings' => [
        'reply' => __('Reply', 'community-press-zone'),
        'cancel' => __('Cancel', 'community-press-zone'),
    ],
]);
```

### File Locations
- `languages/forum-press-zone-{locale}.po`
- `languages/forum-press-zone-{locale}-community-press-zone-admin-app.json`

### Translation Maintenance Rules
| Action | Requirement |
|--------|-------------|
| **Adding new UI string** | Add to all `.po` files, translate to all languages |
| **Removing UI string** | Remove from all `.po` files to avoid bloat |
| **Editing UI string** | Update in all `.po` files, retranslate appropriately |

---

## 4. Security Rules

### Output Escaping (ALWAYS)
```php
esc_html($text)           // HTML content
esc_attr($value)          // HTML attributes
esc_url($url)             // URLs
wp_kses_post($html)       // Post content with allowed HTML
```

### Input Sanitization (ALWAYS)
```php
sanitize_text_field(wp_unslash($_POST['field']))
sanitize_textarea_field(wp_unslash($_POST['content']))
sanitize_key($key)
absint($id)
```

### Nonce Verification
```php
// REST API: X-WP-Nonce header (automatic)

// AJAX
check_ajax_referer('press_zone_{action}', 'nonce');

// Forms
wp_nonce_field('press_zone_{action}');
wp_verify_nonce($_POST['_wpnonce'], 'press_zone_{action}');
```

### Permission Checks
```php
Roles::canModerate($user_id)        // Moderator check
current_user_can('manage_options')  // Admin check
is_user_logged_in()                 // Auth check
```

---

## 5. Common Mistakes to Avoid

| Mistake | Fix |
|---------|-----|
| **JS: Missing window reference in IIFE** | **Get from window: `const PresszoneForumApp = window.PresszoneForumApp`** |
| **Hardcoded colors (even in dark mode!)** | **Use CSS variables from existing definitions** |
| **Not checking existing variables** | **Search `_variables.scss` or `main.css` first** |
| **Using short CSS prefixes (<4 chars)** | **Use `--community-press-zone-*`, `.community-press-zone-*`** |
| Missing text domain | Add `'community-press-zone'` |
| **Forgetting CSS build after SCSS changes** | **Run `npm run build:css` from plugin root** |
| **Forgetting admin build after changes** | **Run `cd admin && npm run build`** |
| Missing output escaping | Use `esc_html()`, etc. |
| Missing input sanitization | Use `sanitize_*()` |
| Wrong table names | Use `press_zone_` prefix |
| Skipping nonce checks | Always verify nonces |
| Direct DB queries | Use `Query` class |
| Creating stubs/placeholders | Implement full functionality |
| **Writing inline toast code** | **Use `Toast.js` component** |
| **Duplicating animation keyframes** | **Use shared ones from `_animations.css`** |
| **Hardcoded animation durations** | **Use `--community-press-zone-duration-*` variables** |
| **Missing reduced motion support** | **Add `@media (prefers-reduced-motion)` rules** |
| **Duplicating validation logic** | **Use `PostCreator->checkExternalLinks()` and `checkWordFilter()`** |
| **Using `.*?` for nested BBCode** | **Use `(?:(?!\[quote).)*?` negative lookahead** |
| **Hash navigation without reload** | **Add `window.location.reload()` after `href` change** |
| **Bare `presszoneForumData` access** | **Use `window.presszoneForumData?.prop \|\| fallback`** |
| **Only inline errors, no toast** | **Always show toast for user-facing errors** |
| **Private validation methods** | **Make reusable validators `public`** |
| **Not syncing TinyMCE before submit** | **Call `tinymce.triggerSave()` first** |
| **Regex for nested HTML structures** | **Use DOMDocument - regex can't handle arbitrary nesting** |
| **forEach on nested DOM elements** | **Use while loop with innermost-first filtering** |
| **Processing outer elements first** | **Always process innermost first, then work outward** |
| **Styling `<a>` as button without specificity** | **Use `a.classname` selector + `color: #fff !important` to override link styles** |
| **Removing animation class on animationend** | **Keep class - removal breaks CSS rules that depend on it (causes secondary animations)** |
| **JS inserting content that PHP also renders** | **Check if PHP template already displays it - avoid double rendering** |
| **Adding JS-generated HTML without CSS** | **Always add corresponding CSS for new element classes created in JS** |
| **Tab slide CSS suppression via class** | **Don't remove slide class after animation - child suppression rules stop working** |
| innerHTML with API data | Use DOMParser or textContent, or add SECURITY comment if server-sanitized |
| **Using `wp_add_inline_style()`** | **Write to static CSS file in `wp-content/uploads/presszone-forum/`** |
| **`<style>` tags in PHP templates** | **Move styles to SCSS files under `assets/scss/`** |
| **Inline `style=` attributes** | **Use CSS utility classes or component classes** |
| `outline: none` without alternative | Use visible `outline: 2px solid` with `:focus-visible` |
| Missing nonce action parameter | Always pass action string: `verifyNonce($nonce, 'action_name')` |
| `file_put_contents()` usage | Use WP Filesystem API |
| SVG icons without aria-label | Add `aria-label` or `role="img" aria-label="..."` to all icon SVGs |
| `javascript:void(0)` links | Use `<button>` elements instead |
| Modal without focus trap | Implement focus trap: trap focus inside, return on close |
| `unserialize()` usage | Use `json_encode()`/`json_decode()` instead |
| Missing `@media (prefers-reduced-motion)` | Add reduced motion media query to disable animations |
| Dropdown without aria-expanded | Add `aria-expanded="true/false"` to all dropdown triggers |
| **Using CSS variable without defining it** | **Add to `_css-variables.scss` :root AND body.community-press-zone-dark blocks** |
| **Hardcoded `rgba()` colors in SCSS** | **Create CSS variable in `_css-variables.scss`, use `var(--community-press-zone-*)` instead** |
| **Defining variable in only light or dark mode** | **EVERY CSS variable needs BOTH `:root` AND `body.community-press-zone-dark` definitions** |
| **Editing one SCSS file, missing duplicates** | **Search ALL scss files: `threads.scss`, `frontend.scss`, `_legacy-frontend.scss`, `compiled/*.scss`, `extracted/*.scss`** |
| **Adding state color without variable** | **Create semantic variables: `--sticky-bg`, `--unread-bg`, etc. with light/dark values** |
| **Renaming CSS variable without checking custom.css** | **Check `writeCustomCss()` in Plugin.php - admin Design page colors will break** |
| **Not verifying Design page after CSS changes** | **Test `/wp-admin/admin.php?page=forum-press-zone#/design` colors still work** |
| **Using `<table>` elements** | **NEVER use `<table>` - always use `<div>` with CSS Grid/Flexbox** |
| **Using `wp_is_mobile()` for layouts** | **Use CSS media queries - never user-agent detection for responsive design** |

---

## 6. Development Workflow

1. **PHP changes** -> Test immediately
2. **Frontend SCSS** -> `npm run build:css` -> Test
3. **Frontend JS** -> Hard refresh -> Test (no build)
4. **Admin SPA** -> `cd admin && npm run build` -> Test
5. **Translations** -> Correct text domain -> Regenerate PO
6. **Database** -> Use migrations in `includes/migrations/`

---

## 7. CSS Architecture Rules (ABSOLUTE BAN ON INLINE CSS)

> **CRITICAL**: NEVER use inline CSS for ANY reason. There are NO exceptions. If you think inline CSS is required, you are wrong - use CSS classes or custom.css instead.

### ABSOLUTELY FORBIDDEN - No Exceptions, No Excuses
```php
// ❌ FORBIDDEN - wp_add_inline_style() - NEVER USE
wp_add_inline_style('handle', $css);  // WRONG - Always use static CSS files

// ❌ FORBIDDEN - <style> tags in templates - NEVER USE
<style>.my-class { color: red; }</style>  // WRONG - Move to SCSS files

// ❌ FORBIDDEN - style="" attributes - NEVER USE
<div style="margin-top: 10px;">  // WRONG - Use CSS classes

// ❌ FORBIDDEN - Any form of dynamic inline CSS - NEVER USE
echo '<div style="color: ' . $color . '">';  // WRONG - Use CSS variables
```

**Why this matters:**
- Inline CSS breaks cacheability
- Inline CSS increases page weight
- Inline CSS makes theming impossible
- Inline CSS violates WordPress.org guidelines
- There is ALWAYS a better solution using classes

### Required Patterns (The ONLY Acceptable Ways)
```php
// ✅ CORRECT - All styles in SCSS files
// assets/scss/components/_my-component.scss
.community-press-zone-my-component {
    margin-top: 10px;
}

// ✅ CORRECT - Use utility classes in templates
<div class="community-press-zone-mt-sm">

// ✅ CORRECT - Dynamic values: Use data attributes + CSS
<div data-depth="<?php echo esc_attr($depth); ?>" class="community-press-zone-post">
// Then in CSS: .community-press-zone-post[data-depth="1"] { margin-left: 12px; }

// ✅ CORRECT - Admin-customized colors: Write to static file
$css_file = wp_upload_dir()['basedir'] . '/presszone-forum/custom.css';
file_put_contents($css_file, $css_content);
wp_enqueue_style('community-press-zone-custom', $css_url);
```

### NO EXCEPTIONS - Solutions for "But I need inline CSS because..."

| "Reason" for inline CSS | Correct Solution |
|-------------------------|------------------|
| "Dynamic color from admin" | Write to `custom.css` file on settings save |
| "User-specific value" | Use CSS classes with predefined values |
| "Calculated dimension" | Use CSS `calc()` with variables |
| "Conditional styling" | Add/remove CSS classes with PHP/JS |
| "One-off override" | Create a specific CSS class |
| "Performance" | Static CSS is MORE performant |
| "Quick fix" | Take time to do it right |

For dynamic values like nesting depth, use CSS classes:
```php
// CORRECT - depth class in HTML
<div class="community-press-zone-post community-press-zone-depth-<?php echo esc_attr($depth); ?>">

// CORRECT - CSS defines each level (depth capped at 6)
.community-press-zone-depth-1 { margin-left: 12px; }
.community-press-zone-depth-2 { margin-left: 24px; }
.community-press-zone-depth-3 { margin-left: 36px; }
```

### File Locations
| Style Type | Location |
|------------|----------|
| Base styles | `assets/scss/base/` |
| Components | `assets/scss/components/` |
| Pages | `assets/scss/pages/` |
| Utilities | `assets/scss/utilities/` |
| Variables | `assets/scss/abstracts/_variables.scss` |
| Admin-customized colors | `wp-content/uploads/presszone-forum/custom.css` |

### CSS Variable Architecture (CRITICAL)

**Source of truth: `assets/scss/abstracts/_css-variables.scss`**

Every CSS variable used ANYWHERE in SCSS must be defined in this file in BOTH blocks:
1. `:root { }` - Light mode values
2. `body.community-press-zone-dark { }` - Dark mode values

### Admin Design Page & custom.css (DO NOT BREAK)

**Admin Design page: `/wp-admin/admin.php?page=forum-press-zone#/design`**

The Design page allows admins to customize colors. These colors are written to:
`wp-content/uploads/presszone-forum/custom.css`

**CRITICAL CONSTRAINT when editing CSS variables:**
- `custom.css` overrides CSS variables defined in `_css-variables.scss`
- The variable NAMES must match exactly between both files
- If you rename a CSS variable, the admin customizations will BREAK
- Always verify Design page colors still work after CSS variable changes

**How it works:**
1. Admin picks colors in Design page → saved to WP options
2. On settings save → `Plugin::writeCustomCss()` generates `custom.css`
3. `custom.css` overrides `:root { --community-press-zone-primary: #newcolor; }`
4. Frontend loads `custom.css` AFTER core styles

**When modifying CSS variables:**
| Action | Impact | Required Check |
|--------|--------|----------------|
| Renaming variable | BREAKS admin overrides | Update `writeCustomCss()` in `class-community-press-zone-plugin.php` |
| Adding new variable | Safe | Add to Design page if user-configurable |
| Removing variable | May break styles | Check if used in `custom.css` output |
| Changing default value | Safe | Admin overrides still work |

**Before using a CSS variable:**
```bash
# Check if it exists
grep "variable-name:" assets/scss/abstracts/_css-variables.scss
```

**Adding new CSS variable:**
```scss
// In _css-variables.scss

:root {
  // Light mode
  --community-press-zone-new-color: #hex-light;
}

body.community-press-zone-dark {
  // Dark mode - REQUIRED
  --community-press-zone-new-color: #hex-dark;
}
```

**NEVER do this:**
```scss
// WRONG - Hardcoded color breaks theming
background: rgba(251, 191, 36, 0.12);

// CORRECT - Use CSS variable
background: var(--community-press-zone-sticky-bg);
```

**SCSS files with duplicate styles (check ALL when editing):**
- `pages/threads.scss` - main source
- `frontend.scss` - legacy compiled
- `_legacy-frontend.scss` - legacy backup
- `compiled/*.scss` - extracted/compiled
- `extracted/*.scss` - extracted CSS

---

## 8. Key Files Reference

| Purpose | File |
|---------|------|
| Main plugin | `forum-press-zone.php` |
| Plugin orchestrator | `includes/class-community-press-zone-plugin.php` |
| REST base | `includes/api/class-community-press-zone-rest-base.php` |
| Database queries | `includes/class-community-press-zone-query.php` |
| Post creation | `includes/class-community-press-zone-post-creator.php` |
| Permissions | `includes/class-community-press-zone-roles.php` |
| **CSS Variables (SOURCE OF TRUTH)** | **`assets/scss/abstracts/_css-variables.scss`** |
| SCSS Variables (mappings) | `assets/scss/abstracts/_variables.scss` |
| Admin CSS variables | `admin/src-vanilla/styles/main.css` (`:root` block) |
| Admin shared animations | `admin/src-vanilla/styles/_animations.css` |
| Admin tab slide animations | `admin/src-vanilla/styles/_tab-slide-animations.css` |
| Admin Tabs component | `admin/src-vanilla/components/Tabs.js` |
| Admin Toast component | `admin/src-vanilla/components/Toast.js` |
| Build config (frontend) | `package.json` |
| Build config (admin) | `admin/package.json` |

---

## 9. Quick Reference - Prefixes (WordPress.org Compliant)

| Context | Prefix | Example |
|---------|--------|---------|
| CSS variables | `--community-press-zone-` | `var(--community-press-zone-primary)` |
| CSS classes | `community-press-zone-` | `.community-press-zone-btn` |
| CSS state classes | `community-press-zone-` | `.community-press-zone-active`, `.community-press-zone-is-open` |
| Dark mode class | `community-press-zone-dark` | `body.community-press-zone-dark` |
| Keyframes | `community-press-zone-` | `@keyframes community-press-zone-spin` |
| JS globals | `presszoneForumCamelCase` | `presszoneForumData` |
| JS app object | `PresszoneForumApp` | `window.PresszoneForumApp.showToast()` |
| JS dark mode API | `PresszoneForumDarkMode` | `window.PresszoneForumDarkMode.toggle()` |
| PHP namespace | `PressZone` | `namespace PressZone;` |
| DB tables | `press_zone_` | `wp_press_zone_posts` |
| WP options | `press_zone_` | `press_zone_board_title` |
| Text domain | `forum-press-zone` | `__('Text', 'community-press-zone')` |

**WordPress.org requires 4+ character prefixes. All code uses `community-press-zone-` prefix.**

Always use role colors when you have a link to a user name in the forum.
