Overview
Start Here
Installation
Tutorial
Architect App Builder
Todo List App
Beacon Beats Audio Store
UCAL App
Authoring
Dynamic Apps
Static Apps
dev_docs
Why Pharos Feels Different
Architect Builder
Framework
Artifacts & Layout
Configuration
Outbound Email
How to Use CLI
Contracts
YAML Authoring
Action Hooks and Middleware
Feature Transaction Primitives
Generated Surfaces
Declarative CRM
Input Validation
Identity and Tenancy
UI Surface Library
Social Connections
Payment Provider Management
Integration Adapters
Reference
Contract, UI, and Surface Reference
Styling UI Elements
Architect Media Artifacts
API Docs
Use Cases
Dynamic SPAs
Generated Admin and APIs
Scheduling and Marketplaces
Multi-App Hosting
Release Notes
Vision
Pharos Framework

← Back

Generated Surfaces

Pharos provides a framework-owned generated surface runtime for tenant-scoped CRUD entities.

The implementation is contract-driven:

  • app fixtures declare the surface contract
  • shared Pharos runtime code matches routes, enforces policy, validates input, and performs state mutation
  • apps only supply fixtures, forms, policies, and host wiring

The executable generated surface kinds are:

  • generated_api
  • generated_admin

Related framework-owned execution families such as declarative actions, workflows, bounded side effects, webhook delivery, and jobs operator reporting are documented through their own contracts rather than this generated-surface fixture envelope.

Only the surface kinds listed above are currently executed by the shared runtime path.

Do not confuse generated surfaces with feature-source bundles. Feature bundles reduce repeated route, page, and fragment-response wiring inside data/features/*.yml, then compile down to ordinary route and action contracts. Generated surfaces remain the framework-owned CRUD execution family described in this document.

When to use generated surfaces

Use generated surfaces when the app needs framework-owned CRUD behavior derived from declared entities, forms, routes, and scoped context rather than hand-authoring every management or API endpoint shape separately.

Use the shipped examples in ucal when you need a larger proving app, and use the broader fixture reference in the App surface reference when you need parameter-level detail.

Validation path

Use this path to validate generated surfaces:

  1. Open apps/ucal/data/contracts/routes.yml.
  2. Open apps/ucal/data/contracts/actions.yml.
  3. Build the app.
  4. Run the tenant API proof.

Commands:

bin/pharos build app ucal --build-dir dist
sh scripts/check.sh ucal tenant-api

Expected result:

  • generated CRUD routes execute through the shared runtime
  • default generated mutation routes do not need to be repeated manually in app
  • source when the generated surface already defines them

  • soft-delete entities gain framework-owned restore behavior
  • wrong-content-type generated mutation requests are rejected through the
  • request-hook contract

Fixture envelope

A generated surface fixture is a YAML document with a surfaces array:

fixture_id: generated_surface_fixture_v1
surfaces:
- surface_id: service_generated_api
  surface_kind: generated_api
  entity_id: service

Each executable surface declares the entity it manages, the scoped context it lives under, the operations it exposes, and the validation and projection rules that the framework must honor.

Required runtime contract shape

Executable generated surfaces use these core fields:

surface_id: service_generated_api
surface_kind: generated_api
entity_id: service
entity_collection: services
form_id: service_form
identity_field: id
identity_param: service_id
scope_field: business_id
context_binding:
  state_collection: businesses
  param: business_slug
  match_field: slug
  context_id_field: id
  context_label_field: name
  tenant_collection: tenants
  tenant_match_field: source_business_id
  tenant_match_from_context_field: id
operations:
  list:
    method: GET
    path: /api/businesses/{business_slug}/services/
    capability: manage_services

Core fields

  • surface_id: stable generated surface id
  • surface_kind: generated_api or generated_admin
  • entity_id: logical entity name
  • entity_collection: state collection mutated by the runtime
  • form_id: shared form contract used for validation/input extraction
  • identity_field: record key field, usually id
  • identity_param: route parameter for item-level operations
  • scope_field: tenant or parent-context foreign key written into records

Context binding

context_binding resolves the route context and tenant scope from state:

  • state_collection: collection to search for the context record
  • param: route parameter name
  • match_field: record field matched against the route parameter
  • context_id_field: field copied into the generated record scope
  • context_label_field: label returned in API responses
  • tenant_collection, tenant_match_field, tenant_match_from_context_field: optional tenant lookup fields for capability enforcement

If the bound context is missing, the runtime returns a clear not-found error instead of mutating global state.

Operations

Operation entries define route matching and policy binding.

When an app ships the default generated-admin or generated-API shape, Pharos can derive the matching build-artifact routes instead of requiring the source route fixture to repeat every create, update, and delete mapping manually. The lower-level route artifact remains inspectable in built data/contracts/routes.json.

Generated API

Supported API operations:

  • list
  • schema
  • create
  • update
  • delete
  • restore for soft-delete entities

When the app-derived generated API surface is present, the default mutation routes are now build-time derivable:

  • <entity_id>_api_create
  • <entity_id>_api_update
  • <entity_id>_api_delete
  • <entity_id>_api_restore when the entity uses soft_delete_field

Those default routes target the ordinary action ids:

  • create_<entity_id>
  • update_<entity_id>
  • delete_<entity_id>
  • restore_<entity_id> when the entity uses soft_delete_field

Derived generated API mutation routes also carry the narrow request-admission hook json_body_required, so the shared runtime rejects non-JSON mutation requests before the generated API handler runs.

Example:

operations:
  list:
    method: GET
    path: /api/businesses/{business_slug}/services/
    capability: manage_services
  schema:
    method: GET
    path: /api/businesses/{business_slug}/services/schema/
    capability: manage_services
  create:
    method: POST
    path: /api/businesses/{business_slug}/services/
    capability: manage_services
  update:
    method: POST
    path: /api/businesses/{business_slug}/services/{service_id}/
    capability: manage_services
  delete:
    method: POST
    path: /api/businesses/{business_slug}/services/{service_id}/delete/
    capability: manage_services
  restore:
    method: POST
    path: /api/businesses/{business_slug}/services/{service_id}/restore/
    capability: manage_services

Generated admin

Supported admin operations:

  • manage
  • create
  • edit
  • update
  • delete
  • restore for soft-delete entities

When the app-derived generated admin surface is present, the default child routes are now build-time derivable from the manage route:

  • <prefix>_create
  • <prefix>_edit
  • <prefix>_update
  • <prefix>_delete
  • <prefix>_restore when the entity uses soft_delete_field

where <prefix> is the manage route id without the trailing _manage.

Example:

operations:
  manage:
    method: GET
    path: /businesses/{business_slug}/services/
    capability: manage_services
  create:
    method: POST
    path: /businesses/{business_slug}/services/new/
    capability: manage_services
  edit:
    method: GET
    path: /businesses/{business_slug}/services/{service_id}/edit/
    capability: manage_services
  update:
    method: POST
    path: /businesses/{business_slug}/services/{service_id}/edit/
    capability: manage_services
  delete:
    method: POST
    path: /businesses/{business_slug}/services/{service_id}/delete/
    capability: manage_services

Every operation may declare a capability. When present, Pharos enforces authentication plus tenant-aware policy evaluation before serving the generated surface.

Authoring checklist

For one generated surface family, make sure the app declares all of these together:

  • the entity or collection being managed
  • the form contract used for input validation
  • the generated surface declaration itself
  • the route declarations binding request paths to that surface
  • any scoped context-binding values needed for parent or tenant resolution
  • any app-owned templates or schema surfaces referenced by the generated path

If one of these pieces is missing, the app may still build partway through authoring, but the generated surface contract will not be complete.

Collection behavior

Generated list surfaces support framework-owned filtering, sorting, projection, and pagination.

Response projection

response_fields controls the JSON fields returned by generated API list responses:

- id
- business_id
- name
- cost
- duration_minutes
- is_active

Filters

filters configures query-string filters:

- query: name
  field: name
  match: contains
  label: Service name
- query: is_active
  field: is_active
  match: boolean
  default: 'true'
  label: Status

Supported filter match kinds:

  • exact
  • contains
  • boolean
  • array_contains_integer

A filter entry may also declare an options array. When present on generated admin surfaces, Pharos renders that filter as a shared framework-owned <select> instead of a free-text input.

Working examples

Use these shipped surfaces when authoring or reviewing generated behavior:

  • apps/ucal/data/contracts/routes.yml
  • apps/ucal/data/contracts/forms.yml
  • apps/ucal/data/contracts/form-fields.yml
  • apps/ucal/data/contracts/entity-fields.yml
  • apps/ucal/templates/api/*.schema.json

Use the fixture and surface list when you want the broader contract inventory. Use the UCAL build and validation commands on this page when you want the exact local proof path.

Sorts

sorts plus default_sort define stable collection ordering:

- key: sort_order_asc
  field: sort_order
  direction: asc
  type: number
  label: Display order
- key: name_asc
  field: name
  direction: asc
  type: string
  label: Name

Supported sort types:

  • string
  • number
  • weekday

A sort entry may also declare secondary_field plus optional secondary_type to keep schedule-like collections stable within the primary ordering.

Pagination

page_size_default and page_size_max control generated collection pagination.

The generated API list response includes:

  • items
  • pagination.page
  • pagination.page_size
  • pagination.total_items
  • pagination.total_pages
  • sort

Write behavior

Generated writes compose shared Pharos validation, relation checks, uniqueness checks, audit logging, and state mutation.

Defaults and coercion

  • defaults supplies framework-owned default values
  • field_types coerces integers, booleans, and integer arrays
  • auto_sequence_field assigns the next in-scope order value on create
  • soft_delete_field marks records inactive instead of removing them
  • restore sets the configured soft_delete_field back to its active value

Validation

Generated surfaces reuse the shared form contract and validation runtime, then layer in surface-specific rules:

  • field_constraints
  • relation_inputs
  • uniqueness_rules

Example:

field_constraints:
- field: cost
  kind: decimal
  rule_id: service_cost_decimal
relation_inputs:
- field: technician_ids
  related_collection: technicians
  related_scope_field: business_id
  label_field: name
  form_checkbox_prefix: technician_ids__
  invalid_rule_id: service_technician_ids_invalid
  scope_rule_id: service_technician_scope_invalid
uniqueness_rules:
- fields:
  - business_id
  - name
  casefold_fields:
  - name
  rule_id: service_name_unique
  message: A service with that name already exists for this business.

Supported field constraint kinds:

  • positive_integer
  • decimal
  • email
  • time
  • date
  • enum

Audit events

Generated writes append audit entries to audit_log. Surface fixtures can override the emitted event names:

audit_events:
  create: generated_service_created
  update: generated_service_updated
  delete: generated_service_deactivated

Generated admin presentation

generated_admin surfaces may define an admin block for titles, navigation, list columns, deletion behavior, presentation copy, and generated-surface composition hints:

admin:
  list_title: Generated Service Admin
  list_intro: Framework-owned generated admin surface for business services.
  back_path_template: /businesses/{business_slug}/
  create_title: Create service
  item_title_field: name
  list_columns:
  - field: sort_order
    label: Display order
  - field: name
    label: Name
  - field: cost
    label: Cost
  - field: duration_minutes
    label: Duration

When list_columns is omitted, Pharos derives a short default list view from response_fields.

The admin block may also override presentation text such as:

  • filter_title
  • records_title
  • create_title
  • create_button_label
  • save_button_label
  • empty_state_text
  • delete_button_label

Admin surfaces may also point at an app-owned composition source under the app root:

admin:
  composition_source: generated_admin/service.yaml

The native loader consumes the JSON-compatible YAML subset for these files so apps can keep composition hints in generated_admin/ without adding app-local runtime code.

Composition sources can override generated-admin section order and records presentation, including:

  • page_wrapper_class
  • filter_form_class
  • create_form_class
  • section_order
  • per-section class
  • per-section title
  • per-section intro
  • per-section form_class
  • records-section records_view
  • records-section records_wrapper_class
  • records-section record_card_class

Current records views are:

  • table
  • card_grid
  • detail_list

Admin surfaces may also declare delete_strategy:

  • soft or omitted: mark the configured soft_delete_field inactive
  • hard: remove the matching record from the collection entirely

Restore routes are emitted only for the soft lifecycle path. Entities that declare delete_strategy: hard do not receive restore operations.

Relation inputs automatically render as scoped checkbox groups in generated admin forms.

Form fields with widget: "select" and an options array render as framework-owned <select> inputs in generated admin forms. This is the shared path used by UCAL weekday availability management.

Form-field presentation may also be customized in the shared path with:

  • label
  • help_text
  • placeholder

Relation-input groups may override shared presentation with:

  • label
  • help_text
  • empty_options_text

Response contracts

Generated API responses use framework-owned envelopes:

  • list: pharos.generated.api.list.v1
  • item: pharos.generated.api.item.v1
  • schema: pharos.generated.api.schema.v1

Validation failures return the shared pharos.validation.report.v1 envelope. Authorization and authentication failures return the shared pharos.error.v1 envelope.

CLI surfaces

Pharos exposes reporting commands for generated surfaces:

pharos/bin/pharos api generate app ucal --build-dir pharos/dist
pharos/bin/pharos api schema app ucal --build-dir pharos/dist
pharos/bin/pharos admin generate app ucal --build-dir pharos/dist

Use these commands to inspect which generated surfaces the framework sees for an app and which schema contract a generated API will publish.

Job queue admin surfaces

Queue management is a framework-owned runtime surface, not an entity-generated pipeline, because job_execution is a runtime JSON state array rather than an app-owned relational entity. UCAL ships the reference wiring:

  • Routes in apps/ucal/data/contracts/routes.yml:
  • - GET /admin/jobs/ — queue list filtered by execution_status, paginated, searchable by job_id/handler_id - GET /admin/jobs/:job_id/ — job detail with job_execution_attempt history, payload, and error messages - POST /admin/jobs/:job_id/cancel/ — cancel a queued job (CSRF-protected) - POST /admin/jobs/:job_id/retry/ — re-enqueue a failed job (CSRF-protected)

  • Capabilities: view_job_queue (staff_admin, business_owner), manage_job_queue (staff_admin) in policy-fixture-v1.yml, with guarded_actions cancel_queue_job / retry_queue_job
  • Page specs and body templates under apps/ucal/templates/queue/
  • Page params builders job_queue_list_page_params_json_dup_runtime / job_queue_detail_page_params_json_dup_runtime in json_runtime_ops.c, wired into resolve_declared_page_params_json_runtime
  • Action handlers cancel_queue_job / retry_queue_job in handle_action_route reuse native_dynamic_job_cancel_report_json / native_dynamic_job_retry_report_json and redirect back to the detail page

The cancel and retry actions record job_cancelled / job_retried workflow execution events, matching the observability contract of the job queue.

Current app coverage

UCAL currently exercises the shared framework path on three tenant-scoped entities:

  • services
  • technicians
  • business availability

All three run through the same framework-owned runtime for:

  • generated admin
  • generated API
  • auth-aware policy checks
  • HTML and JSON validation parity
  • filtering, sorting, and pagination
  • soft delete behavior
  • hard delete behavior where explicitly configured
  • schema export
  • enum-backed validation rules
  • weekday plus secondary-field sorting for schedule-like collections

The reference app supplies declarative fixtures and templates. Generated CRUD behavior lives in shared Rust framework code under src/runtime/ with the compiled pharos CLI as the command surface.