{"navigation":[{"section":"api","documents":[{"slug":"rest-api","title":"REST API","summary":"Classic Semitexa REST endpoints with typed payloads, versioning, and consumer-friendly response shaping.","href":"/docs/api/rest-api"},{"slug":"structured-errors","title":"Structured Errors","summary":"Throw domain exceptions and let semitexa-api map them into stable machine-readable error envelopes.","href":"/docs/api/structured-errors"},{"slug":"active-version","title":"Active Version","summary":"The current collection endpoint with a clean X-Api-Version header and no deprecation noise.","href":"/docs/api/active-version"},{"slug":"sunset-version","title":"Sunset Version","summary":"A deprecated product endpoint that emits both Deprecation and Sunset headers.","href":"/docs/api/sunset-version"},{"slug":"schema-discovery","title":"Schema Discovery","summary":"A mini Swagger-style explorer for the live product API contract, schema endpoint, and response shapes.","href":"/docs/api/schema-discovery"},{"slug":"graphql","title":"GraphQL API","summary":"GraphQL-first Semitexa contracts built with typed payloads and typed output DTOs instead of resolver sprawl.","href":"/docs/api/graphql"},{"slug":"rest-graphql","title":"REST + GraphQL","summary":"One Semitexa use case can serve both REST and GraphQL without duplicating handler logic into separate resolver classes.","href":"/docs/api/rest-graphql"}]},{"section":"auth","documents":[{"slug":"session","title":"Session Auth","summary":"Google signs the user in, then the session stores the selected demo role and re-hydrates it on every request.","href":"/docs/auth/session"},{"slug":"session-payloads","title":"Session Payloads","summary":"Semitexa forbids string-key session chaos: session state lives in typed Session Payloads or it does not exist.","href":"/docs/auth/session-payloads"},{"slug":"google","title":"Google Authorization","summary":"Authorization is required for demo SSE blocks that keep a long-lived backend connection open.","href":"/docs/auth/google"},{"slug":"machine","title":"Machine Auth","summary":"Service-to-service authentication via Bearer tokens — scoped, revocable, and audited.","href":"/docs/auth/machine"},{"slug":"protected","title":"Protected Route","summary":"Add one access attribute and one optional permission attribute and the framework enforces access — 401 for unauthenticated requests, 403 for unauthorized ones.","href":"/docs/auth/protected"},{"slug":"requires-permission","title":"Requires Permission","summary":"Declare one permission slug on the payload and let the framework enforce it before your handler runs.","href":"/docs/auth/requires-permission"},{"slug":"rbac","title":"RBAC","summary":"Hybrid RBAC with coarse-grained capabilities, exact permission slugs, and module-owned permission catalogs.","href":"/docs/auth/rbac"}]},{"section":"cli","documents":[{"slug":"describe-commands","title":"Project Graph Introspection","summary":"Routes, modules, contracts, and handlers can be introspected directly from the CLI instead of reverse-engineering the framework graph by hand.","href":"/docs/cli/describe-commands"},{"slug":"runtime-maintenance","title":"Runtime Maintenance","summary":"Reload workers, clear stale cache, sync registries, lint architecture rules, and probe handler wiring without reaching for ad-hoc shell scripts.","href":"/docs/cli/runtime-maintenance"},{"slug":"scaffolding-generators","title":"Scaffolding Generators","summary":"Scaffold modules, pages, payloads, services, and contracts through commands that already understand Semitexa structure and AI-friendly output modes.","href":"/docs/cli/scaffolding-generators"},{"slug":"workers-scheduling","title":"Workers & Scheduling","summary":"Run queues, scheduler pools, mail delivery, webhooks, and tenant-scoped commands from a coherent operator surface instead of bespoke daemons.","href":"/docs/cli/workers-scheduling"},{"slug":"ai-tooling","title":"AI Tooling Surface","summary":"Semitexa exposes AI-facing commands as explicit CLI contracts: capabilities, skills, log access, and a local assistant entrypoint.","href":"/docs/cli/ai-tooling"},{"slug":"orm-console","title":"ORM Console Toolkit","summary":"The ORM ships with a practical CLI surface: status, diff, sync, and seed commands with dry-run safety and SQL plan export.","href":"/docs/cli/orm-console"}]},{"section":"data","documents":[{"slug":"domain-models","title":"Domain-Level Models","summary":"Semitexa separates persistence resources from business models. Resources map tables; domain models carry behavior and invariants.","href":"/docs/data/domain-models"},{"slug":"repository-workflow","title":"Repository Workflow","summary":"The canonical Semitexa path: handlers depend on repository contracts, repositories return domain models, and persistence resources stay behind the boundary.","href":"/docs/data/repository-workflow"},{"slug":"schema-sync","title":"Schema Sync, Not Migration Churn","summary":"Semitexa creates SQL only when the real schema changed, blocks destructive drops by default, and logs the exact DDL plan as SQL and JSON.","href":"/docs/data/schema-sync"},{"slug":"query","title":"Query Builder","summary":"Compose type-safe queries with a fluent API — no raw SQL, no magic strings.","href":"/docs/data/query"},{"slug":"filtering","title":"Filtering","summary":"Mark a property #[Filterable] and the ORM handles the rest — no manual WHERE clauses.","href":"/docs/data/filtering"},{"slug":"pagination","title":"Pagination","summary":"Offset and cursor pagination out of the box — switch modes with a single query parameter.","href":"/docs/data/pagination"},{"slug":"relations","title":"Relations","summary":"Declare parent and child links on the resource itself, then read typed relations from the handler.","href":"/docs/data/relations"},{"slug":"table-extension","title":"Shared Table Extension","summary":"Two modules can extend one table independently, and the ORM merges the schema without forcing either side to edit the other.","href":"/docs/data/table-extension"},{"slug":"n-plus-one","title":"N+1 Without Magic","summary":"Semitexa avoids N+1 by using resource slices for the exact columns and relations each screen needs, instead of hiding database traffic behind implicit relation loading.","href":"/docs/data/n-plus-one"},{"slug":"orm-guide","title":"ORM Working Guide","summary":"Declaring a resource, querying it, relations and write policies, optimistic locking, transactions, wiring a store, and the gotchas.","href":"/docs/data/orm-guide"}]},{"section":"di","documents":[{"slug":"overview","title":"DI Canon","summary":"One canonical DI path for container-managed classes — protected property attributes, no constructor arguments, validated at boot and enforced by lint:di.","href":"/docs/di/overview"},{"slug":"services","title":"Declaring a Service","summary":"#[AsService] makes a plain class a worker-scoped singleton the container will build and inject — and when you do not need it.","href":"/docs/di/services"},{"slug":"readonly","title":"Readonly Injection","summary":"The default tier — one instance per worker, injected into a protected property, with optional injection for dependencies that may not be installed.","href":"/docs/di/readonly"},{"slug":"mutable","title":"Mutable Injection","summary":"#[ExecutionScoped] opts a class into a per-execution clone; #[InjectAsMutable] marks the properties re-injected on that clone.","href":"/docs/di/mutable"},{"slug":"factory","title":"Factory Injection","summary":"#[InjectAsFactory] injects a ContractFactory that selects among a contract's implementations by backed-enum key — not a closure, and not a new instance per call.","href":"/docs/di/factory"},{"slug":"configuration","title":"Configuration Injection","summary":"#[Config] reads a scalar from the environment into a typed property, with a default in code — the only supported way a container-managed class reads env.","href":"/docs/di/configuration"},{"slug":"contracts","title":"Service Contracts","summary":"One module declares an interface, implementations advertise themselves with SatisfiesServiceContract, and the active one is visible in contracts:list.","href":"/docs/di/contracts"},{"slug":"contract-resolution","title":"Resolving Service Contracts","summary":"Seeing which implementation is bound to a contract, using a resolver as a factory, and the Factory* naming convention for choosing by key.","href":"/docs/di/contract-resolution"},{"slug":"discovery-contributors","title":"Discovery Contributors","summary":"How a package teaches boot-time discovery to recognise its own attribute, with priority ordering and per-class error isolation.","href":"/docs/di/discovery-contributors"}]},{"section":"events","documents":[{"slug":"arena","title":"Execution Arena","summary":"Launch the same backend intent in sync, Swoole async, and queued modes, then watch the proof arrive over SSE.","href":"/docs/events/arena"},{"slug":"sync","title":"Sync Events","summary":"Dispatch an event and all sync listeners run before the response is sent.","href":"/docs/events/sync"},{"slug":"deferred","title":"Deferred Handler","summary":"Heavy work runs after the response is sent — the user gets instant feedback.","href":"/docs/events/deferred"},{"slug":"queued","title":"Queued Handler","summary":"Events survive restarts and scale across workers — backed by a durable message queue.","href":"/docs/events/queued"},{"slug":"sse","title":"SSE Stream","summary":"Real-time server push without WebSockets — connect once and receive real backend events over plain HTTP.","href":"/docs/events/sse"},{"slug":"ledger","title":"Ledger Demo","summary":"Dispatch a protected demo event and inspect only the persisted demo ledger rows through a safe read-only view.","href":"/docs/events/ledger"},{"slug":"pipeline","title":"Request Pipeline Events","summary":"The events every request passes through -- AuthCheck, AccessCheck, HandleRequest -- and the Swoole server lifecycle hooks around them.","href":"/docs/events/pipeline"},{"slug":"dispatch-configuration","title":"Domain Event Dispatch and Configuration","summary":"Choosing sync or async per handler, the HandlerCompleted event, running the async worker, and how the three event kinds compare.","href":"/docs/events/dispatch-configuration"}]},{"section":"get-started","documents":[{"slug":"installation","title":"Installation","summary":"Create the project, review the baseline env contract, and bring up the Semitexa runtime the supported way.","href":"/docs/get-started/installation"},{"slug":"local-domain","title":"Local Domain","summary":"Register .test domains through the built-in local-domain helper instead of relying on ad hoc host setup.","href":"/docs/get-started/local-domain"},{"slug":"module-structure","title":"Module Structure","summary":"The minimal Semitexa module is a typed HTTP spine of payload, handler, resource, and template.","href":"/docs/get-started/module-structure"},{"slug":"base-tenant","title":"Base Tenant","summary":"Establish one default tenant context early so tenant-aware behavior is visible before the rest of the application grows.","href":"/docs/get-started/base-tenant"},{"slug":"locale-setup","title":"Locale Setup","summary":"Configure the minimal Locale contract so translations and locale-aware rendering become explicit early.","href":"/docs/get-started/locale-setup"},{"slug":"ai-console","title":"AI Console","summary":"Use the Semitexa AI console as a command translation surface over real operator commands.","href":"/docs/get-started/ai-console"},{"slug":"beyond-controllers","title":"Beyond Controllers","summary":"Understand why Semitexa keeps transport, use case, and rendering as separate explicit responsibilities.","href":"/docs/get-started/beyond-controllers"}]},{"section":"llm","documents":[{"slug":"overview","title":"LLM Module Overview","summary":"What `semitexa/llm` adds to the framework and how your project can expose its own CLI skills to the assistant.","href":"/docs/llm/overview"},{"slug":"providers","title":"Providers & Backends","summary":"Provider contracts, backend resolution, local vs remote Ollama, and the environment knobs that shape LLM runtime behavior.","href":"/docs/llm/providers"},{"slug":"skills","title":"Adding Skills","summary":"How a console command becomes AI-executable through `#[AsAiSkill]`, metadata policy, and registry discovery.","href":"/docs/llm/skills"},{"slug":"execution-flow","title":"Execution Flow","summary":"How a user request becomes a planner decision, a reviewed skill proposal, and finally a real console execution.","href":"/docs/llm/execution-flow"},{"slug":"skill-contract","title":"AsAiSkill Contract","summary":"What a class must satisfy to be discovered and invoked as an AI skill.","href":"/docs/llm/skill-contract"},{"slug":"prompt-reference","title":"LLM Prompt Reference","summary":"The prompt surface the platform exposes to language models.","href":"/docs/llm/prompt-reference"}]},{"section":"migration","documents":[{"slug":"post-hardening","title":"Post-Hardening Migration Guide","summary":"Migrate from the legacy access model and webhook flow to Semitexa's current architecture — explicit access attributes, tenant-aware webhooks, atomic replay protection, and the unified quality gate.","href":"/docs/migration/post-hardening"}]},{"section":"platform","documents":[{"slug":"tenancy-resolution","title":"Tenant Context Resolution","summary":"See how Semitexa resolves the active tenant from subdomain, header, path, or query input before the rest of the platform runs.","href":"/docs/platform/tenancy-resolution"},{"slug":"tenancy-config","title":"Per-Tenant Configuration","summary":"Three demo tenants with distinct branding -- switch tenant, everything changes without if/else.","href":"/docs/platform/tenancy-config"},{"slug":"tenancy-layers","title":"Multi-Layer Tenancy","summary":"Organization, Locale, Theme, Environment -- four independent layers compose into one TenantContext.","href":"/docs/platform/tenancy-layers"},{"slug":"tenancy-isolation","title":"Data Isolation","summary":"Product listing scoped by tenant -- switch tenant, list changes. Zero manual WHERE clauses.","href":"/docs/platform/tenancy-isolation"},{"slug":"tenancy-queue","title":"Queue Tenant Propagation","summary":"Tenant context travels with queued jobs -- _tenant key injected automatically, restored by worker.","href":"/docs/platform/tenancy-queue"},{"slug":"skin-generation","title":"Skin Generation","summary":"Generating a skin: inputs, the generation pipeline and what lands on disk.","href":"/docs/platform/skin-generation"},{"slug":"skin-algorithms","title":"Skin Algorithms and Knobs","summary":"The algorithms behind skin generation and every knob you can turn.","href":"/docs/platform/skin-algorithms"},{"slug":"skin-refinement","title":"Skin Refinement","summary":"Refining an existing skin with skin:refine instead of regenerating it.","href":"/docs/platform/skin-refinement"}]},{"section":"project-graph","documents":[{"slug":"overview","title":"Project Graph Overview","summary":"Understand what `semitexa-project-graph` adds: a stored structural map, an intelligence layer, and task-scoped context for large-codebase work.","href":"/docs/project-graph/overview"},{"slug":"inspection","title":"Inspecting the Graph","summary":"Use Project Graph queries and intelligence views to inspect modules, dependencies, flows, events, and hotspots without reconstructing the repository manually.","href":"/docs/project-graph/inspection"},{"slug":"impact","title":"Impact, Context, and Watch Mode","summary":"Use impact analysis, context packing, and watch mode to scope risky changes and keep graph-backed answers current during long work sessions.","href":"/docs/project-graph/impact"},{"slug":"architecture","title":"Project Graph Architecture","summary":"How the graph is built: parsers, the extractor pipeline, node and edge types, and which attribute produces which node.","href":"/docs/project-graph/architecture"},{"slug":"commands","title":"Project Graph Command Reference","summary":"Every ai:review-graph command with its options, output shape and a worked example.","href":"/docs/project-graph/commands"},{"slug":"intelligence","title":"Graph Intelligence","summary":"How the graph scores relevance, traces flows and infers intent, and what the intelligence commands return.","href":"/docs/project-graph/intelligence"},{"slug":"ai-integration","title":"Using the Graph as AI Context","summary":"Feeding graph slices to an assistant: what to ask for, what comes back, and how it beats grep.","href":"/docs/project-graph/ai-integration"}]},{"section":"prompt","documents":[{"slug":"overview","title":"Prompt Catalog Overview","summary":"What `semitexa/prompt` adds to the framework — an ORM-style catalog that turns inline prompt strings into addressable, versionable, override-aware records.","href":"/docs/prompt/overview"},{"slug":"catalog","title":"Defining Prompts","summary":"Declare a prompt with `#[AsPrompt]` and a standalone Twig body; the catalog discovers it like any other framework attribute.","href":"/docs/prompt/catalog"},{"slug":"rendering","title":"Rendering & Self-Binding","summary":"How PromptRenderer compiles a prompt with Twig, and how a self-binding prompt exposes its typed data to the template through getter dot-access.","href":"/docs/prompt/rendering"},{"slug":"overrides","title":"Per-Tenant Overrides","summary":"A DB-backed override layer lets each tenant edit a prompt on top of the code catalog, with version history and restore.","href":"/docs/prompt/overrides"},{"slug":"cli","title":"Prompt CLI","summary":"Inspect, render, override, and evaluate catalog prompts from the console.","href":"/docs/prompt/cli"}]},{"section":"reference","documents":[{"slug":"README","title":"Reference","summary":"What each part of the public surface is, generated from the code itself.","href":"/docs/reference/README"},{"slug":"attributes-api","title":"Api attributes","summary":"Every attribute declared by `Api`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-api"},{"slug":"attributes-auth","title":"Auth attributes","summary":"Every attribute declared by `Auth`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-auth"},{"slug":"attributes-authorization","title":"Authorization attributes","summary":"Every attribute declared by `Authorization`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-authorization"},{"slug":"attributes-core","title":"Core attributes","summary":"Every attribute declared by `Core`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-core"},{"slug":"attributes-graphql","title":"Graphql attributes","summary":"Every attribute declared by `Graphql`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-graphql"},{"slug":"attributes-ledger","title":"Ledger attributes","summary":"Every attribute declared by `Ledger`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-ledger"},{"slug":"attributes-llm","title":"Llm attributes","summary":"Every attribute declared by `Llm`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-llm"},{"slug":"attributes-media","title":"Media attributes","summary":"Every attribute declared by `Media`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-media"},{"slug":"attributes-orm","title":"Orm attributes","summary":"Every attribute declared by `Orm`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-orm"},{"slug":"attributes-platformui","title":"PlatformUi attributes","summary":"Every attribute declared by `PlatformUi`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-platformui"},{"slug":"attributes-projectgraph","title":"ProjectGraph attributes","summary":"Every attribute declared by `ProjectGraph`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-projectgraph"},{"slug":"attributes-prompt","title":"Prompt attributes","summary":"Every attribute declared by `Prompt`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-prompt"},{"slug":"attributes-scheduler","title":"Scheduler attributes","summary":"Every attribute declared by `Scheduler`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-scheduler"},{"slug":"attributes-ssr","title":"Ssr attributes","summary":"Every attribute declared by `Ssr`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-ssr"},{"slug":"attributes-tenancy","title":"Tenancy attributes","summary":"Every attribute declared by `Tenancy`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-tenancy"},{"slug":"attributes-testing","title":"Testing attributes","summary":"Every attribute declared by `Testing`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-testing"},{"slug":"attributes-update","title":"Update attributes","summary":"Every attribute declared by `Update`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-update"},{"slug":"attributes-webhooks","title":"Webhooks attributes","summary":"Every attribute declared by `Webhooks`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-webhooks"},{"slug":"attributes-workflow","title":"Workflow attributes","summary":"Every attribute declared by `Workflow`, with the signature the code has and a usage quoted from the codebase.","href":"/docs/reference/attributes-workflow"},{"slug":"commands-ai","title":"ai commands","summary":"Every `ai` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-ai"},{"slug":"commands-cache","title":"cache commands","summary":"Every `cache` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-cache"},{"slug":"commands-contracts","title":"contracts commands","summary":"Every `contracts` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-contracts"},{"slug":"commands-demo","title":"demo commands","summary":"Every `demo` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-demo"},{"slug":"commands-dev","title":"dev commands","summary":"Every `dev` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-dev"},{"slug":"commands-docs","title":"docs commands","summary":"Every `docs` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-docs"},{"slug":"commands-general","title":"general commands","summary":"Every `general` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-general"},{"slug":"commands-ledger","title":"ledger commands","summary":"Every `ledger` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-ledger"},{"slug":"commands-lint","title":"lint commands","summary":"Every `lint` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-lint"},{"slug":"commands-local-app","title":"local-app commands","summary":"Every `local-app` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-local-app"},{"slug":"commands-local-domain","title":"local-domain commands","summary":"Every `local-domain` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-local-domain"},{"slug":"commands-local-router","title":"local-router commands","summary":"Every `local-router` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-local-router"},{"slug":"commands-logs","title":"logs commands","summary":"Every `logs` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-logs"},{"slug":"commands-mail","title":"mail commands","summary":"Every `mail` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-mail"},{"slug":"commands-make","title":"make commands","summary":"Every `make` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-make"},{"slug":"commands-media","title":"media commands","summary":"Every `media` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-media"},{"slug":"commands-openapi","title":"openapi commands","summary":"Every `openapi` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-openapi"},{"slug":"commands-orm","title":"orm commands","summary":"Every `orm` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-orm"},{"slug":"commands-os","title":"os commands","summary":"Every `os` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-os"},{"slug":"commands-platform-ui","title":"platform-ui commands","summary":"Every `platform-ui` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-platform-ui"},{"slug":"commands-playground","title":"playground commands","summary":"Every `playground` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-playground"},{"slug":"commands-prompt","title":"prompt commands","summary":"Every `prompt` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-prompt"},{"slug":"commands-queue","title":"queue commands","summary":"Every `queue` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-queue"},{"slug":"commands-registry","title":"registry commands","summary":"Every `registry` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-registry"},{"slug":"commands-routes","title":"routes commands","summary":"Every `routes` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-routes"},{"slug":"commands-scaffold","title":"scaffold commands","summary":"Every `scaffold` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-scaffold"},{"slug":"commands-scheduler","title":"scheduler commands","summary":"Every `scheduler` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-scheduler"},{"slug":"commands-server","title":"server commands","summary":"Every `server` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-server"},{"slug":"commands-sitemap","title":"sitemap commands","summary":"Every `sitemap` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-sitemap"},{"slug":"commands-skins","title":"skins commands","summary":"Every `skins` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-skins"},{"slug":"commands-system","title":"system commands","summary":"Every `system` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-system"},{"slug":"commands-tasks","title":"tasks commands","summary":"Every `tasks` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-tasks"},{"slug":"commands-tenant","title":"tenant commands","summary":"Every `tenant` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-tenant"},{"slug":"commands-test","title":"test commands","summary":"Every `test` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-test"},{"slug":"commands-theme","title":"theme commands","summary":"Every `theme` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-theme"},{"slug":"commands-ui-playground","title":"ui-playground commands","summary":"Every `ui-playground` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-ui-playground"},{"slug":"commands-update","title":"update commands","summary":"Every `update` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-update"},{"slug":"commands-weave","title":"weave commands","summary":"Every `weave` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-weave"},{"slug":"commands-webhook","title":"webhook commands","summary":"Every `webhook` command, with the arguments and options the console actually defines.","href":"/docs/reference/commands-webhook"},{"slug":"events","title":"Events","summary":"Every event the runtime routes, named by the listeners that subscribe to it.","href":"/docs/reference/events"}]},{"section":"rendering","documents":[{"slug":"philosophy","title":"SSR Philosophy","summary":"Semitexa SSR is one continuous rendering architecture: page, slots, deferred regions, live refresh, and interactive components stay inside one server-owned story.","href":"/docs/rendering/philosophy"},{"slug":"resource-dtos","title":"Resource DTOs","summary":"A Resource DTO is the one typed source of presentation data: handlers shape it once, templates consume it everywhere, and no view has to dissect random arrays.","href":"/docs/rendering/resource-dtos"},{"slug":"slots","title":"Slot Resources","summary":"Each page region is its own resource pipeline with the same template system as the main page — no scattered partial glue, no mystery wiring.","href":"/docs/rendering/slots"},{"slug":"components","title":"Components","summary":"Reusable, attribute-registered UI components — discovered automatically from the classmap.","href":"/docs/rendering/components"},{"slug":"seo","title":"SEO","summary":"Set title, description, and Open Graph tags from your handler — no template hacks needed.","href":"/docs/rendering/seo"},{"slug":"assets","title":"Asset Pipeline","summary":"Declare assets with glob patterns in assets.json — served, versioned, and injected automatically.","href":"/docs/rendering/assets"},{"slug":"component-scripts","title":"Component Script Assets","summary":"A Semitexa SSR component can own its optional enhancement asset, so behavior travels with the component instead of leaking into page-level glue.","href":"/docs/rendering/component-scripts"},{"slug":"deferred-scripts","title":"Script Injection","summary":"Deferred blocks carry their own JS — injected once when the block arrives, never duplicated.","href":"/docs/rendering/deferred-scripts"},{"slug":"deferred","title":"Deferred Blocks","summary":"SSR renders the shell first, then expensive regions stream in as real HTML over SSE — no SPA handoff and no client-side page rebuild.","href":"/docs/rendering/deferred"},{"slug":"deferred-encapsulation","title":"Block Isolation","summary":"Two identical blocks on the same page run independently — scoped DOM, scoped JS, no conflicts.","href":"/docs/rendering/deferred-encapsulation"},{"slug":"deferred-live","title":"Live Widgets","summary":"A live slot can refresh itself on a timer while the page stays SSR-first — no SPA runtime and no handwritten polling layer.","href":"/docs/rendering/deferred-live"},{"slug":"reactive-report","title":"Reactive Report","summary":"Background work updates an SSR-first slot in place, so the UI feels live without falling back to SPA state orchestration.","href":"/docs/rendering/reactive-report"},{"slug":"reactive-import","title":"Reactive Import","summary":"Background batches keep moving, and the page reflects server progress as live HTML instead of a client-managed progress app.","href":"/docs/rendering/reactive-import"},{"slug":"reactive-analytics","title":"Reactive Analytics","summary":"Independent analytics jobs can light up one dashboard progressively, while the page stays server-rendered from the first byte.","href":"/docs/rendering/reactive-analytics"},{"slug":"reactive-ai","title":"Reactive AI Task","summary":"Submit a task and watch the AI pipeline stages reveal one by one as the cron job processes it.","href":"/docs/rendering/reactive-ai"},{"slug":"ui-primitives","title":"UI Primitives","summary":"The atomic ui=\"...\" vocabulary -- button, input, label, field-shell, surface, badge -- and the attribute-driven runtime behind it.","href":"/docs/rendering/ui-primitives"},{"slug":"ui-composition","title":"UI Composition: UiPart and UiSlot","summary":"How a component declares the primitives it is made of, how part props resolve, and how bind, slots and inputProps behave.","href":"/docs/rendering/ui-composition"},{"slug":"ui-events","title":"UI Events and Transport","summary":"Declaring events with UiOn, the signed render-time manifest, the capture-only frontend runtime, the HTTP dispatch endpoint and the SSE push channel.","href":"/docs/rendering/ui-events"},{"slug":"ui-forms","title":"UI Forms","summary":"Composing platform.form, the authoritative submit pipeline, per-field signed projection and the playground.","href":"/docs/rendering/ui-forms"},{"slug":"ui-grammar","title":"UI Grammar Reference","summary":"The attribute grammar a component author writes: what each attribute means and where it is allowed.","href":"/docs/rendering/ui-grammar"},{"slug":"ui-ssr-integration","title":"UI and SSR Integration","summary":"How platform-ui primitives and components sit on top of the SSR module.","href":"/docs/rendering/ui-ssr-integration"},{"slug":"template-cache","title":"Twig Template Cache","summary":"How the Twig cache behaves under long-running workers and when you need to clear it.","href":"/docs/rendering/template-cache"}]},{"section":"routing","documents":[{"slug":"basic","title":"Basic Route","summary":"Define a route with one access attribute on the payload — no XML, no YAML, no config files.","href":"/docs/routing/basic"},{"slug":"adding-routes","title":"Adding Pages and Routes","summary":"Creating a module and its first route end to end: JSON and HTML responses, where each class goes, how discovery finds it, a custom 404, and the usual mistakes.","href":"/docs/routing/adding-routes"},{"slug":"parameterized","title":"Parameterized Route","summary":"Path parameters with regex constraints, how the hydrator injects them, and why a default does not make a segment optional.","href":"/docs/routing/parameterized"},{"slug":"env-route-override","title":"Env Route Override","summary":"Keep the payload as the route source of truth while allowing operations to remap the public URL through .env.","href":"/docs/routing/env-route-override"},{"slug":"payload-shield","title":"Payload As A Shield","summary":"Hydration happens before the handler, and each setter owns the normalization and guard logic for its own field.","href":"/docs/routing/payload-shield"},{"slug":"payload-parts","title":"Payload Parts","summary":"One module owns the route, another module can extend the same payload contract without forking or reopening the base class.","href":"/docs/routing/payload-parts"},{"slug":"content-negotiation","title":"Content Negotiation","summary":"One endpoint, multiple response formats — automatically.","href":"/docs/routing/content-negotiation"},{"slug":"public-endpoint","title":"Public Payload","summary":"Anonymous endpoints opt in explicitly with the public access attribute. Every other payload requires authentication.","href":"/docs/routing/public-endpoint"},{"slug":"debugging","title":"Debugging Routes","summary":"Three commands that answer why a route is not matching, what it resolves to, and which template finally renders it.","href":"/docs/routing/debugging"}]},{"section":"runtime","documents":[{"slug":"state-lifecycle","title":"Runtime State Lifecycle","summary":"Per-request, per-worker, persistent, and test-only state — the Swoole-aware taxonomy that decides which caches reset between requests, which survive across requests, and which only reset when a test explicitly asks.","href":"/docs/runtime/state-lifecycle"},{"slug":"sessions-and-cookies","title":"Sessions and Cookies","summary":"The session contract and its Swoole Table storage, typed session segments, flash messages, and reading and writing cookies.","href":"/docs/runtime/sessions-and-cookies"},{"slug":"request-context","title":"Request Context: Tenant, Auth, Locale","summary":"Reaching the per-request tenant, auth and locale -- through the request-scoped container or statically -- and when each is still at its default.","href":"/docs/runtime/request-context"}]},{"section":"testing","documents":[{"slug":"payload-contracts","title":"Payload Contract Testing","summary":"Run one project-level contract suite through the canonical test runner and let strategy profiles verify payload boundaries without hand-writing repetitive negative cases.","href":"/docs/testing/payload-contracts"}]},{"section":"validation","documents":[{"slug":"core-validators","title":"Core Validators","summary":"Trait-based validation primitives covering presence, type, string, format, numeric, datetime, choice, collection, comparison, conditional, composite, and domain rules — composed into payload `validate()` methods that the framework runs automatically before handlers.","href":"/docs/validation/core-validators"},{"slug":"ui-field-validation","title":"UI Field Validation","summary":"The server-side rules DSL for platform-ui fields, including cross-field rules such as sameAsField.","href":"/docs/validation/ui-field-validation"},{"slug":"payload-validation","title":"Payload Validation","summary":"Where validation sits in the pipeline, how payload rules and helpers are written, hydration, the failure response, and session and cookie payloads.","href":"/docs/validation/payload-validation"}]}],"doc_title":"Adding Pages and Routes","doc_summary":"Creating a module and its first route end to end: JSON and HTML responses, where each class goes, how discovery finds it, a custom 404, and the usual mistakes.","doc_html":"<article class=\"sx-docs-fragment\" data-doc-id=\"routing/adding-routes\" data-doc-locale=\"en\">\n<h1>Adding Pages and Routes</h1>\n<p><strong>Put new routes in modules</strong> — <code>src/modules/</code>, <code>packages/</code>, or an installed package under <code>vendor/semitexa/</code>.</p>\n<p>This is a convention, not a mechanical limit. <code>ClassDiscovery</code> merges every PSR-4 directory under <code>src/</code>, <code>tests/</code>, <code>packages/</code> and <code>vendor/semitexa/</code>, so a payload dropped straight into <code>src/</code> with an access attribute <em>will</em> be discovered and <em>will</em> answer requests. The reason to use a module anyway is that everything defining a route then lives in one predictable layout with a clear namespace, which is what the graph, the generators and the structure validator all read. A route class sitting loose in <code>src/</code> works and is invisible to all of them.</p>\n<p>If you are looking for where the boundary actually is: it is the discovery roots above. Nothing outside them is scanned.</p>\n<hr />\n<h2>Step-by-step: create a new module and add a route</h2>\n<ol>\n<li>\n<p><strong>Create the module directory</strong><br />\nExample: <code>src/modules/Website/</code> (or <code>Api</code>, <code>Blog</code>, etc.).</p>\n</li>\n<li>\n<p><strong>Add <code>composer.json</code> inside the module</strong><br />\nSo the framework recognises it as a Semitexa module and registers its autoload:</p>\n<pre><code class=\"language-json\">{\n  &quot;name&quot;: &quot;semitexa/module-website&quot;,\n  &quot;type&quot;: &quot;semitexa-module&quot;,\n  &quot;autoload&quot;: {\n    &quot;psr-4&quot;: {\n      &quot;Semitexa\\\\Modules\\\\Website\\\\&quot;: &quot;.&quot;\n    }\n  }\n}\n</code></pre>\n<p>(Project root <code>composer.json</code> uses a single mapping <code>&quot;Semitexa\\\\Modules\\\\&quot;: &quot;src/modules/&quot;</code> for all modules.)</p>\n<p>Run <code>composer dump-autoload</code> in the <strong>project root</strong> after adding or changing module <code>composer.json</code>.</p>\n</li>\n<li>\n<p><strong>Create Request (Payload) and Handler in the module</strong><br />\nPut <strong>HTTP request DTOs</strong> in <strong><code>Application/Payload/Request/</code></strong> (namespace <code>Semitexa\\Modules\\{ModuleName}\\Application\\Payload\\Request\\</code>). Put <strong>HTTP handlers</strong> in <strong><code>Application/Handler/PayloadHandler/</code></strong>. See <a href=\"../get-started/module-structure.md\">module structure</a> for the full layout.</p>\n<p><strong>Example Request</strong> — e.g. <code>src/modules/Website/Application/Payload/Request/HomePayload.php</code>:</p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace Semitexa\\Modules\\Website\\Application\\Payload\\Request;\n\nuse Semitexa\\Core\\Attribute\\AsPublicPayload;\nuse Semitexa\\Core\\Contract\\PayloadInterface;\nuse Semitexa\\Modules\\Website\\Application\\Resource\\HomeResource;\n\n#[AsPublicPayload(path: '/', methods: ['GET'], responseWith: HomeResource::class)]\nclass HomePayload implements PayloadInterface\n{\n}\n</code></pre>\n<p><strong>Example Handler</strong> — e.g. <code>src/modules/Website/Application/Handler/PayloadHandler/HomeHandler.php</code>:</p>\n<pre><code class=\"language-php\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace Semitexa\\Modules\\Website\\Application\\Handler\\PayloadHandler;\n\nuse Semitexa\\Core\\Attribute\\AsPayloadHandler;\nuse Semitexa\\Core\\Contract\\PayloadInterface;\nuse Semitexa\\Core\\Contract\\ResourceInterface;\nuse Semitexa\\Core\\Response;\nuse Semitexa\\Modules\\Website\\Application\\Payload\\Request\\HomePayload;\nuse Semitexa\\Modules\\Website\\Application\\Resource\\HomeResource;\n\n#[AsPayloadHandler(payload: HomePayload::class, resource: HomeResource::class)]\nclass HomeHandler\n{\n    public function handle(PayloadInterface $request, ResourceInterface $response): ResourceInterface\n    {\n        return Response::json(['message' =&gt; 'Hello from Website module']);\n    }\n}\n</code></pre>\n<p>Use the <strong>recommended</strong> layout: <strong><code>Application/Payload/Request/</code></strong> for HTTP request DTOs, <strong><code>Application/Resource/</code></strong> for response DTOs, <strong><code>Application/Handler/PayloadHandler/</code></strong> for HTTP handlers, <strong><code>Application/View/templates/</code></strong> for Twig. See <a href=\"../get-started/module-structure.md\">module structure</a> for the canonical layout (payloads, event handlers, pipeline). The class must live under the <strong>module namespace</strong> (<code>Semitexa\\Modules\\Website\\...</code>) and the module must have a valid <code>composer.json</code> with <code>&quot;type&quot;: &quot;semitexa-module&quot;</code> and PSR-4 autoload.</p>\n<p>The example above returns JSON. <strong>For HTML pages</strong> use a Response DTO with a Twig template — see the section <strong>&quot;Responses: JSON and HTML pages&quot;</strong> below (or AI_REFERENCE / guides in semitexa/docs).</p>\n</li>\n<li>\n<p><strong>Reload / clear stale runtime state if needed</strong><br />\nAfter adding or changing Request (Payload) classes, treat this as ordinary code changes: reload the app or restart the container if your runtime has not picked them up yet. Do <strong>not</strong> treat <code>bin/semitexa registry:sync</code> as a required manual step for ordinary payload changes.</p>\n</li>\n<li>\n<p><strong>Reload</strong><br />\nRestart the app (e.g. <code>bin/semitexa server:stop</code> then <code>bin/semitexa server:start</code>) or ensure your runtime picks up the new classes; the framework will discover the new Request/Handler from the module.</p>\n</li>\n</ol>\n<hr />\n<h2>Responses: JSON and HTML pages</h2>\n<p>The step-by-step example above uses <code>Response::json([...])</code> — suitable for API endpoints. For <strong>HTML pages</strong> the renderer is <strong><code>semitexa/ssr</code></strong>, which ships with the framework. Do not implement your own Twig renderer in the project.</p>\n<p><strong>Steps for HTML pages:</strong></p>\n<ol>\n<li>Create a resource class carrying <code>#[AsResource(handle: '...', template: '@namespace/pages/thing.html.twig')]</code>.</li>\n<li>Store templates in the module under <code>Application/View/templates/</code>; they are addressed through the Twig namespace, not a filesystem path.</li>\n<li>The handler fills the resource context and returns it; <code>LayoutRenderer</code> renders the template.</li>\n</ol>\n<p>A working example is <code>Semitexa\\Demo\\Application\\Resource\\Response\\DemoFeatureResource</code>, which declares\n<code>template: '@project-layouts-semitexa-demo/pages/feature.html.twig'</code>.</p>\n<p><strong>Recommended stack:</strong> for HTML apps, <code>semitexa/core</code> plus <code>semitexa/ssr</code>.</p>\n<p><strong>Detailed docs:</strong> <a href=\"../rendering/philosophy.md\">rendering philosophy</a>, <a href=\"../rendering/resource-dtos.md\">resource DTOs</a> and <a href=\"../rendering/slots.md\">slots</a>. Do not put raw HTML in the handler and do not create a custom renderer — return a resource DTO.</p>\n<p>If you need the public URL shape to be editable per environment without changing PHP code, see <a href=\"env-route-override.md\">env route override</a>. <code>#[AsPublicPayload(path: 'env::VAR::/fallback')]</code> is the canonical pattern.</p>\n<hr />\n<h2>Where to put Request/Handler</h2>\n<table>\n<thead>\n<tr>\n<th>Location</th>\n<th>Discovered for routes?</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Modules:</strong> <code>src/modules/{ModuleName}/</code> (with <code>composer.json</code> <code>type: semitexa-module</code>)</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td><strong>Packages:</strong> project <code>packages/</code> (Semitexa packages with <code>composer.json</code>)</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td><strong>Vendor:</strong> installed packages (e.g. <code>vendor/semitexa/...</code>)</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td><strong>Project <code>src/</code> (namespace <code>App\\</code>), outside a module</strong></td>\n<td>Yes — discovered, but invisible to the graph, generators and structure validator</td>\n</tr>\n</tbody>\n</table>\n<p>Place <strong>all new routes</strong> in a module (existing or new) under <code>src/modules/</code>, in <code>packages/</code>, or in an installed package. Loose classes in the project root still route, but they opt out of every tool that reads the module layout, so treat that as a mistake rather than a shortcut.</p>\n<hr />\n<h2>How discovery works (architecture)</h2>\n<ul>\n<li><strong>ModuleRegistry</strong> finds modules in: <code>src/modules/</code>, project <code>packages/</code>, and <code>vendor/</code> (packages with <code>type: semitexa-module</code> or under <code>vendor/semitexa/</code>).</li>\n<li><strong>IntelligentAutoloader</strong> and <strong>AttributeDiscovery</strong> scan every PSR-4 directory merged by <code>ClassDiscovery</code>: <code>src/</code> (including the project <code>App\\</code> namespace), <code>tests/</code>, <code>packages/</code>, and <code>vendor/semitexa/</code>. Module namespaces are the convention, not the filter.</li>\n<li>So to add new routes you must have a <strong>module</strong> with a proper <code>composer.json</code> and PSR-4 (root: <code>Semitexa\\Modules\\</code> → <code>src/modules/</code>; per-module e.g. <code>Semitexa\\Modules\\Website\\</code> → <code>.</code>). Adding <code>App\\Request\\*</code> / <code>App\\Handler\\*</code> in project <code>src/</code> is not a supported way to register routes.</li>\n</ul>\n<hr />\n<h2>Custom 404 page (error.404 route)</h2>\n<p>If no route matches the request, or a handler throws <code>Semitexa\\Core\\Http\\Exception\\NotFoundException</code>, the system looks for a <strong>named route</strong> <code>error.404</code> (<code>RoutePhase::ROUTE_NAME_404</code>; the older <code>Application::ROUTE_NAME_404</code> is deprecated). If it exists, that route’s handlers run against the same request, so a module can render a custom 404 page. <code>semitexa/ssr</code> already ships one — route index <code>error.404</code>, payload <code>DefaultNotFoundPagePayload</code>.</p>\n<ul>\n<li><strong>Register a Payload</strong> with <code>name: 'error.404'</code> and path/methods as needed (e.g. path <code>'/404'</code>, methods <code>['GET']</code>), plus a Response class and handler that render your 404 view.</li>\n<li><strong>Throw</strong> <code>Semitexa\\Core\\Http\\Exception\\NotFoundException</code> in any handler when a resource is missing; the framework will then dispatch to the <code>error.404</code> route if registered, or return a plain 404 response.</li>\n</ul>\n<hr />\n<h2>Common mistakes / FAQ</h2>\n<p><strong>My payload in <code>src/</code> routes, but no tooling sees it.</strong><br />\nThat is expected. <code>src/</code> is scanned, so the route works, but the module-structure validator, the project graph and the generators all key off the module layout. Move the class into <code>src/modules/{Module}/</code> with a <code>composer.json</code> (<code>&quot;type&quot;: &quot;semitexa-module&quot;</code> and PSR-4 autoload) and it rejoins them.</p>\n<p><strong>I added a new Payload and Handler but the route doesn't exist (404)?</strong><br />\nFirst verify the class lives inside a discovered module, the namespace matches the module PSR-4 mapping, and the app/container was reloaded after the change. <code>registry:sync</code> is a maintenance command, not the default fix for ordinary payload changes.</p>\n<p><strong>Can I patch <code>IntelligentAutoloader</code> or <code>AttributeDiscovery</code> to widen discovery?</strong><br />\nDo not patch vendor. The discovery roots (<code>src/</code>, <code>tests/</code>, <code>packages/</code>, <code>vendor/semitexa/</code>) already cover every location a Semitexa project is expected to use.</p>\n<h2>Summary</h2>\n<ul>\n<li><strong>New pages and routes belong in modules</strong> (<code>src/modules/</code>, <code>packages/</code>, or <code>vendor/semitexa/</code>) — not because loose classes fail to route, but because they drop out of every tool that reads the module layout.</li>\n<li>Each module: directory, <code>composer.json</code> with <code>&quot;type&quot;: &quot;semitexa-module&quot;</code> and PSR-4 (e.g. <code>Semitexa\\Modules\\Website\\</code> → <code>.</code>); root has <code>Semitexa\\Modules\\</code> → <code>src/modules/</code>. Then Request/Handler classes with route attributes in that namespace.</li>\n<li><strong>After adding or changing Payloads:</strong> reload the running app if needed. Use <code>registry:sync</code> only for maintenance flows explicitly documented by a package.</li>\n</ul>\n</article>","section":"routing","__page_document_html_iri":"/docs/routing/adding-routes","__page_document_json_iri":"/docs/routing/adding-routes?_format=json","__page_alternates":[{"type":"application/json","href":"/docs/routing/adding-routes?_format=json"}]}