Files
rspade_system/docs/CLAUDE.dist.md
root f6ac36c632 Enhance refactor commands with controller-aware Route() updates and fix code quality violations
Add semantic token highlighting for 'that' variable and comment file references in VS Code extension
Add Phone_Text_Input and Currency_Input components with formatting utilities
Implement client widgets, form standardization, and soft delete functionality
Add modal scroll lock and update documentation
Implement comprehensive modal system with form integration and validation
Fix modal component instantiation using jQuery plugin API
Implement modal system with responsive sizing, queuing, and validation support
Implement form submission with validation, error handling, and loading states
Implement country/state selectors with dynamic data loading and Bootstrap styling
Revert Rsx::Route() highlighting in Blade/PHP files
Target specific PHP scopes for Rsx::Route() highlighting in Blade
Expand injection selector for Rsx::Route() highlighting
Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls
Update jqhtml packages to v2.2.165
Add bundle path validation for common mistakes (development mode only)
Create Ajax_Select_Input widget and Rsx_Reference_Data controller
Create Country_Select_Input widget with default country support
Initialize Tom Select on Select_Input widgets
Add Tom Select bundle for enhanced select dropdowns
Implement ISO 3166 geographic data system for country/region selection
Implement widget-based form system with disabled state support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 06:21:56 +00:00

32 KiB

RSpade Framework - AI/LLM Development Guide

PURPOSE: Essential directives for AI/LLM assistants developing RSX applications with RSpade.

What is RSpade?

Visual Basic-like development for PHP/Laravel. Think: VB6 apps → VB6 runtime → Windows = RSX apps → RSpade runtime → Laravel.

Philosophy: Modern anti-modernization. While JavaScript fragments into complexity and React demands quarterly paradigm shifts, RSpade asks: "What if coding was easy again?" Business apps need quick builds and easy maintenance, not bleeding-edge architecture.

Important: RSpade is built on Laravel but diverges significantly. Do not assume Laravel patterns work in RSX without verification.

Terminology: RSpade = Complete framework | RSX = Your application code in /rsx/


CRITICAL RULES

🔴 RSpade Builds Automatically - NEVER RUN BUILD COMMANDS

RSpade is an INTERPRETED framework - like Python or PHP, changes are automatically detected and compiled on-the-fly. There is NO manual build step.

ABSOLUTELY FORBIDDEN (unless explicitly instructed):

  • npm run compile / npm run build - DO NOT EXIST
  • bin/publish - Creates releases for OTHER developers (not for testing YOUR changes)
  • rsx:bundle:compile - Bundles compile automatically in dev mode
  • rsx:manifest:build - Manifest rebuilds automatically in dev mode
  • ANY command with "build", "compile", or "publish"

How it works:

  1. Edit JS/SCSS/PHP files
  2. Refresh browser
  3. Changes are live (< 1 second)

If you find yourself wanting to run build commands: STOP. You're doing something wrong. Changes are already live.

🔴 Framework Updates

php artisan rsx:framework:pull  # 5-minute timeout required

Updates take 2-5 minutes. Includes code pull, manifest rebuild, bundle recompilation. For AI: Always use 5-minute timeout.

🔴 Fail Loud - No Silent Fallbacks

ALWAYS fail visibly. No redundant fallbacks, silent failures, or alternative code paths.

// ❌ CATASTROPHIC
try { $clean = Sanitizer::sanitize($input); }
catch (Exception $e) { $clean = $input; }  // DISASTER

// ✅ CORRECT
$clean = Sanitizer::sanitize($input);  // Let it throw

SECURITY-CRITICAL: If sanitization/validation/auth fails, NEVER continue. Always throw immediately.

🔴 No Defensive Coding

Core classes ALWAYS exist. Never check.

// ❌ BAD: if (typeof Rsx !== 'undefined') { Rsx.Route(...) }
// ✅ GOOD: Rsx.Route(...)

🔴 Static-First Philosophy

Classes are namespacing tools. Use static unless instances needed (models, resources). Avoid dependency injection.

🔴 Git Workflow - Framework is READ-ONLY

NEVER modify /var/www/html/system/ - It's like node_modules or the Linux kernel.

  • App repo: /var/www/html/.git (you control)
  • Framework: /var/www/html/system/ (submodule, don't touch)
  • Your code: /var/www/html/rsx/ (all changes here)

Commit discipline: ONLY commit when explicitly asked. Commits are milestones, not individual changes.

🔴 DO NOT RUN rsx:clean

RSpade's cache auto-invalidates on file changes. Running rsx:clean causes 30-60 second rebuilds with zero benefit.

When to use: Only on catastrophic corruption, after framework updates (automatic), or when explicitly instructed.

Correct workflow: Edit → Save → Reload browser → See changes (< 1 second)

🔴 Trust Code Quality Rules

Each rsx:check rule has remediation text that tells AI assistants exactly what to do:

  • Some rules say "fix immediately"
  • Some rules say "present options and wait for decision"

AI should follow the rule's guidance precisely. Rules are deliberately written and well-reasoned.


NAMING CONVENTIONS

Enforced by rsx:check:

Context Convention Example
PHP Methods/Variables underscore_case user_name
PHP Classes Like_This User_Controller
JavaScript Classes Like_This User_Card
Files lowercase_underscore user_controller.php
Database Tables lowercase_plural users
Constants UPPERCASE MAX_SIZE

Critical: Never create same-name different-case files.


DIRECTORY STRUCTURE

/var/www/html/
├── rsx/                        # YOUR CODE
│   ├── app/                    # Modules
│   ├── models/                 # Database models
│   ├── public/                 # Static files (web-accessible)
│   ├── resource/               # Framework-ignored
│   └── theme/                  # Global assets
└── system/                     # FRAMEWORK (read-only)

Special Directories (Path-Agnostic)

resource/ - ANY directory named this is framework-ignored. Store helpers, docs, third-party code. Exception: /rsx/resource/config/ IS processed.

public/ - ANY directory named this is web-accessible, framework-ignored. 5min cache, 30d with ?v=.

Path-Agnostic Loading

Classes found by name, not path. No imports needed.

$user = User_Model::find(1);  // Framework finds it
// NOT: use Rsx\Models\User_Model;  // Auto-generated

CONFIGURATION

Two-tier system:

  • Framework: /system/config/rsx.php (never modify)
  • User: /rsx/resource/config/rsx.php (your overrides)

Merged via array_merge_deep(). Common overrides: development.auto_rename_files, bundle_aliases, console_debug.


ROUTING & CONTROLLERS

class Frontend_Controller extends Rsx_Controller_Abstract
{
    #[Auth('Permission::anybody()')]
    #[Route('/', methods: ['GET'])]
    public static function index(Request $request, array $params = [])
    {
        return rsx_view('Frontend_Index', [
            'bundle' => Frontend_Bundle::render()
        ]);
    }
}

Rules: Only GET/POST allowed. Use :param syntax. All routes MUST have #[Auth].

#[Auth] Attribute

#[Auth('Permission::anybody()')]           // Public
#[Auth('Permission::authenticated()')]     // Require login
#[Auth('Permission::has_role("admin")')]   // Custom

Controller-wide: Add to pre_dispatch(). Multiple attributes = all must pass.

Type-Safe URLs

// PHP
Rsx::Route('User_Controller', 'show', ['id' => 123]);
Rsx::Route('User_Controller', 'show', 123);  // Integer shorthand for 'id'

// JavaScript (identical)
Rsx.Route('User_Controller', 'show', {id: 123});
Rsx.Route('User_Controller', 'show', 123);  // Integer shorthand for 'id'

BLADE & VIEWS

@rsx_id('Frontend_Index')  {{-- Every view starts with this --}}

<body class="{{ rsx_body_class() }}">  {{-- Adds view class --}}

@rsx_include('Component_Name')  {{-- Include by name, not path --}}

SCSS Pairing

/* frontend_index.scss - Same directory as view */
.Frontend_Index {  /* Matches @rsx_id */
    .content { padding: 20px; }
}

JAVASCRIPT

Auto-Initialization

class Frontend_Index {
    static async on_app_ready() {
        // DOM ready
    }

    static async on_jqhtml_ready() {
        // Components ready
    }
}

CRITICAL: JavaScript only executes when bundle rendered.


BUNDLE SYSTEM

One bundle per page required.

class Frontend_Bundle extends Rsx_Bundle_Abstract
{
    public static function define(): array
    {
        return [
            'include' => [
                'jquery',                    // Required
                'lodash',                    // Required
                'rsx/theme/variables.scss',  // Order matters
                'rsx/app/frontend',          // Directory
                'rsx/models',                // For JS stubs
            ],
        ];
    }
}

Auto-compiles on page reload in development.

<head>
    {!! Frontend_Bundle::render() !!}
</head>

JQHTML COMPONENTS

Philosophy

For mechanical thinkers who see structure, not visuals. Write <User_Card> not <div class="card">. Name what things ARE.

Template Syntax

🔴 CRITICAL: <Define> IS the element, not a wrapper

<!-- ✅ CORRECT - Define becomes button -->
<Define:Save_Button tag="button" class="btn btn-primary">
    Save
</Define:Save_Button>

<!-- Renders as: -->
<button class="Save_Button Jqhtml_Component btn btn-primary">Save</button>

Interpolation: <%= escaped %> | <%!= unescaped %> | <% javascript %>

Conditional Attributes (v2.2.162+): Apply attributes conditionally using <% if (condition) { %>attr="value"<% } %> directly in attribute context. Works with static values, interpolations, and multiple conditions per element. Example: <input <% if (this.args.required) { %>required="required"<% } %> />

🔴 CRITICAL on_load() Rules

ONLY modify this.data - NO other properties, NO DOM manipulation

class User_Card extends Jqhtml_Component {
    async on_load() {
        // ✅ ONLY this.data modifications
        this.data = await User_Controller.get_data({id: this.args.id});
        this.data.loaded = true;

        // ❌ FORBIDDEN
        // this.state = {loading: false};      // NO other properties
        // this.$id('title').text(...);        // NO DOM manipulation
        // $.ajax({url: '/api/...'});          // NO direct ajax
    }

    on_ready() {
        // ✅ DOM manipulation here
        this.$id('title').text(this.data.name);
    }
}

Lifecycle

  1. on_create() → Setup default state BEFORE template (sync)
  2. render → Template executes with initialized state
  3. on_render() → Hide uninitialized UI (sync)
  4. on_load() → Fetch data into this.data (async)
  5. on_ready() → DOM manipulation safe (async)

on_create() now runs first - Initialize this.data properties here so templates can safely reference them:

on_create() {
    this.data.rows = [];      // Prevents "not iterable" errors
    this.data.loading = true; // Template can check loading state
}

Double-render: If on_load() modifies this.data, component renders twice (defaults → populated).

Loading Pattern

async on_load() {
    const result = await Product_Controller.list({page: 1});
    this.data.products = result.products;
    this.data.loaded = true;  // Simple flag at END
}
<% if (!this.data.loaded) { %>
    Loading...
<% } else { %>
    <!-- Show data -->
<% } %>

NEVER call this.render() in on_load() - automatic re-render happens.

Attributes

  • $quoted="string" → String literal
  • $unquoted=expression → JavaScript expression
  • $id="name" → Scoped element ID
this.$id('button').on('click', ...);  // Access scoped element

Common Pitfalls

  1. <Define> IS the element - use tag="" attribute
  2. this.data starts empty {}
  3. ONLY modify this.data in on_load()
  4. Use Controller.method() not $.ajax()
  5. Blade components self-closing only
  6. on_create/render/destroy must be sync

Bundle Integration Required

{!! Frontend_Bundle::render() !!}  {{-- Required for JS --}}
<User_Card user_id="123" />       {{-- Now JS executes --}}

For advanced topics: php artisan rsx:man jqhtml


FORM COMPONENTS

Form components use the vals() dual-mode pattern for getting/setting values:

class My_Form extends Jqhtml_Component {
    vals(values) {
        if (values) {
            // Setter - populate form
            this.$id('name').val(values.name || '');
            this.$id('email').val(values.email || '');
            return null;
        } else {
            // Getter - extract values
            return {
                name: this.$id('name').val(),
                email: this.$id('email').val()
            };
        }
    }
}

Validation: Form_Utils.apply_form_errors(form.$, errors) - Matches by name attribute.


MODALS

Basic dialogs:

await Modal.alert("File saved");
if (await Modal.confirm("Delete?")) { /* confirmed */ }
let name = await Modal.prompt("Enter name:");

Form modals:

const result = await Modal.form({
    title: "Edit User",
    component: "User_Form",
    component_args: {data: user},
    on_submit: async (form) => {
        const values = form.vals();
        const response = await User_Controller.save(values);

        if (response.errors) {
            Form_Utils.apply_form_errors(form.$, response.errors);
            return false; // Keep open
        }

        return response.data; // Close and return
    }
});

Requirements: Form component must implement vals() and include <div $id="error_container"></div>.

Details: php artisan rsx:man modals


JQUERY EXTENSIONS

RSpade extends jQuery with utility methods:

Element existence: $('.element').exists() instead of .length > 0

Component traversal: this.$.shallowFind('.Widget') - Finds child elements matching selector that don't have another element of the same class as a parent between them and the component. Prevents selecting widgets from nested child components.

// Use case: Finding form widgets without selecting nested widgets
this.$.shallowFind('.Form_Field').each(function() {
    // Only processes fields directly in this form,
    // not fields in nested sub-forms
});

Sibling component lookup: $('.element').closest_sibling('.Widget') - Searches for elements within progressively higher ancestors. Like .closest() but searches within ancestors instead of matching them. Stops at body tag. Useful for component-to-component communication.

Form validation: $('form').checkValidity() instead of $('form')[0].checkValidity()

Click override: .click() automatically calls e.preventDefault(). Use .click_allow_default() for native behavior.

For complete details: php artisan rsx:man jquery


MODELS & DATABASE

No Mass Assignment

// ✅ CORRECT
$user = new User_Model();
$user->email = $email;
$user->save();

// ❌ WRONG
User_Model::create(['email' => $email]);

Enums

public static $enums = [
    'status_id' => [
        1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active'],
    ],
];

// Usage
$user->status_id = User_Model::STATUS_ACTIVE;
echo $user->status_label;  // "Active"

Migrations

Forward-only, no rollbacks.

php artisan make:migration:safe create_users_table
php artisan migrate:begin
php artisan migrate
php artisan migrate:commit

AJAX ENDPOINTS

#[Ajax_Endpoint]
public static function get_data(Request $request, array $params = [])
{
    return ['success' => true, 'data' => ...];
}
const result = await Demo_Controller.get_data({user_id: 123});

Model Fetch

#[Ajax_Endpoint_Model_Fetch]
public static function fetch($id)
{
    if (!RsxAuth::check()) return false;
    return static::find($id);
}
const user = await User_Model.fetch(1);

AUTHENTICATION

Always use RsxAuth, never Laravel Auth or $_SESSION.

RsxAuth::check();     // Is authenticated
RsxAuth::user();      // User model
RsxAuth::id();        // User ID

Sessions persist 365 days. Never implement "Remember Me".


JAVASCRIPT DECORATORS

/** @decorator */
function logCalls(target, key, descriptor) { /* ... */ }

class Service {
    @logCalls
    @mutex
    async save() { /* ... */ }
}

COMMANDS

Module Creation

rsx:app:module:create <name>              # /name
rsx:app:module:feature:create <m> <f>     # /m/f
rsx:app:component:create --name=x         # Component

Development

rsx:check          # Code quality
rsx:debug /page    # Test routes
rsx:man <topic>    # Documentation
db:query "SQL" --json

Debugging

  • rsx_dump_die() - Debug output
  • console_debug("CHANNEL", ...) - Channel logging
  • CONSOLE_DEBUG_FILTER=CHANNEL - Filter output

ERROR HANDLING

if (!$expected) {
    shouldnt_happen("Class {$expected} missing");
}

Use for "impossible" conditions that indicate broken assumptions.


CODE QUALITY

Professional UI: Hover effects ONLY on buttons, links, form fields. Static elements remain static.

Run rsx:check before commits. Enforces naming, prohibits animations on non-actionable elements.


MAIN_ABSTRACT MIDDLEWARE

Optional /rsx/main.php:

class Main extends Main_Abstract
{
    public function init() { }                   // Bootstrap once
    public function pre_dispatch($request, $params) { return null; } // Before routes
    public function unhandled_route($request, $params) { }          // 404s
}

KEY REMINDERS

  1. Fail loud - No silent failures
  2. Static by default - Unless instances needed
  3. Path-agnostic - Reference by name
  4. Bundles required - For JavaScript
  5. Use RsxAuth - Never Laravel Auth
  6. No mass assignment - Explicit only
  7. Forward migrations - No rollbacks
  8. Don't run rsx:clean - Cache auto-invalidates
  9. All routes need #[Auth] - No exceptions

GETTING HELP

php artisan rsx:man <topic>  # Detailed docs
php artisan list rsx          # All commands

Topics: bundle_api, jqhtml, routing, migrations, console_debug, model_fetch, vs_code_extension, deployment, framework_divergences

Remember: RSpade prioritizes simplicity and rapid development. When in doubt, choose the straightforward approach.

CRITICAL RULES (EXPANDED)

Git Working Directory Rules

# All code changes in /var/www/html/rsx
cd /var/www/html
php artisan rsx:check   # Run commands from project root
git add -A              # Stage from project root
git commit -m "Snapshot: description"

When rsx:clean Actually Fails

The Cost of Running rsx:clean:

  • Entire cache is destroyed (not just invalidated)
  • 30-60 second rebuild on next request
  • Timeouts during development
  • No benefit (changes already visible without clearing)

NAMING CONVENTIONS (EXPANDED)

Class Naming Philosophy

All class names must identify:

  1. Module (e.g., Frontend_, Backend_)
  2. Feature (e.g., Users, Settings)
  3. Type (e.g., _Controller, _Model)

Example: Frontend_Users_Index_Controller → Module: Frontend, Feature: Users, Subfeature: Index, Type: Controller

DIRECTORY STRUCTURE (EXPANDED)

/var/www/html/                     # Project root
├── rsx/                           # YOUR APPLICATION CODE
│   ├── app/                       # Application modules
│   │   ├── frontend/              # Public website (/)
│   │   ├── backend/               # Admin panel (/admin)
│   │   └── api/                   # API endpoints
│   ├── lib/                       # Shared libraries
│   ├── models/                    # Database models
│   ├── public/                    # Static files (web-accessible)
│   │   └── public_ignore.json     # Patterns blocked from HTTP
│   ├── resource/                  # Framework-ignored files
│   │   └── config/                # Configuration overrides
│   │       └── rsx.php            # Merged with /system/config/rsx.php
│   ├── theme/                     # Global theme assets
│   │   ├── variables.scss
│   │   ├── layouts/
│   │   └── components/
│   ├── main.php                   # App-wide middleware (optional)
│   └── permission.php             # Authorization methods
│
└── system/                        # FRAMEWORK CODE (do not modify)
    ├── app/RSpade/                # RSpade framework runtime
    ├── config/                    # Framework configuration
    ├── storage/                   # Build artifacts and caches
    └── artisan                    # Laravel's artisan CLI

Path-Agnostic Implications

  • Move files freely - Location doesn't matter
  • No path management - No use statements needed (auto-generated)
  • Name-based referencing - Just use the class name
  • Uniqueness required - Each class name must be unique

CONFIGURATION (EXPANDED)

Merging Example

// Framework: /system/config/rsx.php
'bundle_aliases' => ['core' => CoreBundle::class]

// User: /rsx/resource/config/rsx.php  
'bundle_aliases' => ['my-app' => MyAppBundle::class]

// Result: Both present
config('rsx.bundle_aliases'); // ['core' => CoreBundle, 'my-app' => MyAppBundle]

ROUTING & CONTROLLERS (EXPANDED)

Route Parameters

Use :param syntax (not Laravel's {param}):

#[Route('/users/:id')]
public static function show_user(Request $request, array $params = [])
{
    $user_id = $params['id'];  // Route parameter
    $tab = $params['tab'];      // Query string parameter
}

Controller-Wide Auth

#[Auth('Permission::authenticated()')]
public static function pre_dispatch(Request $request, array $params = [])
{
    return null;  // Continue to route
}

Custom Permission Methods

Add to /rsx/permission.php:

class Permission extends Permission_Abstract
{
    public static function has_role(Request $request, array $params, string $role): mixed
    {
        if (!RsxAuth::check()) return false;
        return RsxAuth::user()->role === $role;
    }
}

BLADE & VIEWS (EXPANDED)

View Functions

// ✅ GOOD
return rsx_view('Frontend_Index', ['data' => $data]);

// ❌ AVOID
return view('rsx.app.frontend.frontend_index', ['data' => $data]);

Why SCSS Class Wrapping

  • Scoping - Prevents style conflicts between views
  • Specificity - Styles only apply when that view is rendered
  • Organization - Clear ownership of styles

JAVASCRIPT (EXPANDED)

JavaScript File Structure Rules

// ✅ ALLOWED
const API_URL = "https://api.example.com";
function processData(data) { /* ... */ }

// ❌ NOT ALLOWED
const RANDOM = Math.random();  // Function call
let variable = 42;             // Use const

BUNDLE SYSTEM (EXPANDED)

Unified Include System

The include array auto-detects:

  1. Module Aliases - jquery, lodash, bootstrap5, jqhtml
  2. Bundle Aliases - From config('rsx.bundle_aliases')
  3. Bundle Classes - Full class names like Shared_Bundle
  4. Files - Specific file paths
  5. Directories - All files in directory

Include Order Matters

'include' => [
    'rsx/theme/variables.scss',  // Variables first
    'rsx/theme/components',      // Components using variables
    'rsx/app/frontend',          // Pages using components
],

JQHTML COMPONENTS (EXPANDED)

Incremental Scaffolding

Undefined components work immediately - they render as div with the component name as a class.

<Dashboard>
  <Stats_Panel />
  <Recent_Activity />
</Dashboard>

Why on_load() Restrictions Exist

  • this.data triggers automatic re-render when modified
  • DOM may not be fully initialized during on_load()
  • Setting other properties breaks the component lifecycle

Execution Order

  • First: on_create() runs before anything else (setup state)
  • Top-down: render, on_render (parent before children)
  • Bottom-up: on_load, on_ready (children before parent)
  • Parallel: Siblings at same depth process simultaneously during on_load()

this.args vs this.data

// <User_Card $user_id="123" $theme="dark" />
class User_Card extends Jqhtml_Component {
    async on_load() {
        // this.args.user_id = "123" (from attribute)
        // this.data starts as {} (empty)
        this.data = await User_Controller.get_user({id: this.args.user_id});
    }
}

MODELS & DATABASE (EXPANDED)

Model Definition

class User_Model extends Rsx_Model_Abstract
{
    protected $table = 'users';
    protected $fillable = [];  // Always empty - no mass assignment

    public static $enums = [
        'status_id' => [
            1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active'],
        ],
    ];
}

Migration Example

public function up()
{
    DB::statement("
        CREATE TABLE articles (
            id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
            title VARCHAR(255) NOT NULL,
            status_id TINYINT(1) NOT NULL DEFAULT 1,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_status_id (status_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    ");
}

AJAX ENDPOINTS (EXPANDED)

Creating Detailed Endpoints

#[Auth('Permission::anybody()')]
#[Ajax_Endpoint]
public static function get_user_data(Request $request, array $params = [])
{
    $user = User_Model::find($params['user_id']);
    if (!$user) {
        return ['success' => false, 'error' => 'User not found'];
    }
    return ['success' => true, 'user' => $user];
}

Testing Ajax endpoints: php artisan rsx:ajax Controller action --site-id=1 --args='{"id":1}'

Test endpoints behind auth/site scoping or invoke RPC calls from scripts. JSON-only output.

  • Default: Raw response
  • --debug: HTTP-like wrapper
  • --show-context: Display context before JSON

Model Fetch Security

#[Ajax_Endpoint_Model_Fetch]
public static function fetch($id)
{
    if (!RsxAuth::check()) return false;
    $product = static::find($id);
    if ($product && !Permission::can_view_product($product)) {
        return false;
    }
    return $product;
}

AUTHENTICATION (EXPANDED)

RsxAuth Full API

use App\RSpade\Core\Auth\RsxAuth;

if (RsxAuth::check()) {
    $user = RsxAuth::user();     // User model or null
    $user_id = RsxAuth::id();    // User ID or null
    $session = RsxAuth::session(); // Session model or null
}

RsxAuth::login($user);
RsxAuth::logout();

Where to Check Authentication

  1. Controller pre_dispatch() - Protects all routes in controller
  2. /rsx/main.php pre_dispatch() - Global/pattern-based protection
  3. Route method - Individual route protection

JAVASCRIPT DECORATORS (EXPANDED)

Built-in @mutex Decorator

class DataService {
    @mutex
    async save_data() {
        // Only one save_data() call per instance at a time
    }

    @mutex('critical_operation')
    async update_shared_resource() {
        // Only one update across all instances
    }
}

COMMANDS (EXPANDED)

Module Hierarchy

Command Creates Route
rsx:app:module:create Module + index /name
rsx:app:module:feature:create Feature /m/f
rsx:app:submodule:create Submodule + layout /m/s
rsx:app:component:create --name=x jqhtml component N/A

Migration Commands

make:migration:safe name       # Create whitelisted migration
migrate:begin                  # Start snapshot session
migrate                        # Run migrations
migrate:commit                 # Commit changes

DEBUGGING TOOLS (EXPANDED)

rsx:debug - Route Testing

php artisan rsx:debug /dashboard
php artisan rsx:debug /dashboard --user=1          # Test as specific user
php artisan rsx:debug /page --expect-element="#btn" # Verify element exists

console_debug() - Channel-Based Logging

console_debug("AUTH", "Login attempt", $user->id);
console_debug('AJAX', 'Request sent', url, params);

CODE QUALITY (EXPANDED)

Key Quality Rules

  • NoAnimationsRule - No CSS animations/hover on non-actionable elements
  • DuplicateCaseFilesRule - Detect same-name different-case files (critical)
  • MassAssignmentRule - Prohibit $fillable arrays

Trust the Rule Text

Each rule's remediation message specifies exactly how to handle violations:

  • What the problem is
  • Why it matters
  • How to fix it
  • Whether to fix autonomously or present options

For AI assistants: Follow the rule's guidance precisely. Don't override with "common sense" - the rule text is authoritative and deliberately written.

MAIN_ABSTRACT MIDDLEWARE (EXPANDED)

Execution Order

  1. Main::init() - Once at bootstrap
  2. Main::pre_dispatch() - Before every route
  3. Controller::pre_dispatch() - Before specific controller
  4. Route method - If both return null
  5. Main::unhandled_route() - If no route matches

FRAMEWORK DIVERGENCES FROM LARAVEL

CRITICAL: RSpade diverges significantly from Laravel. Key differences:

  • No Eloquent ORM features - Use explicit field assignment only
  • No mass assignment - $fillable arrays always empty
  • No resource routes - Only explicit GET/POST routes
  • No middleware classes - Use Main_Abstract and pre_dispatch()
  • No service providers - Framework handles all bootstrapping
  • No facades - Direct class access only
  • No dependency injection - Static methods preferred

When Laravel documentation conflicts with this guide, this guide takes precedence.

ATTRIBUTES PHILOSOPHY

PHP attributes are metadata only - never define actual attribute classes.

#[Route('/')]           // Framework reads via reflection
#[Auth('...')]          // Never create Route or Auth classes
#[Ajax_Endpoint]        // Just metadata markers

Add stubs to .vscode/attribute-stubs.php for IDE support, but never create the actual classes.

SILENT SUCCESS PHILOSOPHY

Supplemental operations succeed silently - Unix principle "no news is good news".

  • Primary operations report success (what user explicitly requested)
  • Supplemental checks/validations are silent when successful
  • Maintenance operations quiet unless they fail
// Primary operation - reports success
$this->info("Migration completed successfully");

// Supplemental operation - silent on success
$this->validate_cache();  // Only outputs if problem found

BROWSER ERROR LOGGING

JavaScript errors auto-captured when LOG_BROWSER_ERRORS=true in .env.

// Manual error logging
Debugger.log_error('Component failed', error, {user_id: 123});

// Automatic capture of uncaught errors
// Batched and rate-limited to prevent spam

REFACTORING COMMANDS

# Rename PHP classes across entire codebase
php artisan rsx:refactor:rename_php_class Old_Class_Name New_Class_Name

# Automatically updates:
# - All PHP file references
# - All Blade file references  
# - Renames the file itself

BUNDLE include_routes

For scanning additional directories for routes:

class Admin_Bundle extends Rsx_Bundle_Abstract
{
    public static function define(): array
    {
        return [
            'include' => [...],
            'include_routes' => [
                'rsx/lib/admin_tools',  // Scan for #[Route] attributes
            ],
        ];
    }
}

DEVELOPMENT VS PRODUCTION

Development (auto-detected):

  • Cache auto-invalidates on file changes
  • Detailed error messages with stack traces
  • Source maps enabled for debugging
  • Bundle recompilation on every request

Production:

  • Pre-compiled bundles from cache
  • Minimal error messages
  • No source maps
  • Optimized performance

Never manually switch modes - framework auto-detects based on environment.