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
Today the runtime supports these executable generated surface kinds:
generated_apigenerated_admin
Other generated surface kinds may still appear in app graph fixtures as forward-looking contract nodes, but they are not yet executed by the shared runtime path.
Fixture envelope
A generated surface fixture is a JSON 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
The current runtime expects these core fields for executable generated surfaces:
{
"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 idsurface_kind:generated_apiorgenerated_adminentity_id: logical entity nameentity_collection: state collection mutated by the runtimeform_id: shared form contract used for validation/input extractionidentity_field: record key field, usuallyididentity_param: route parameter for item-level operationsscope_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 recordparam: route parameter namematch_field: record field matched against the route parametercontext_id_field: field copied into the generated record scopecontext_label_field: label returned in API responsestenant_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.
Generated API
The current runtime supports these API operations:
listschemacreateupdatedelete
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"
}
}
}
Generated admin
The current runtime supports these admin operations:
managecreateeditupdatedelete
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.
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 today:
exactcontainsbooleanarray_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. This is the proving path used for UCAL weekday availability filtering.
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 today:
stringnumberweekday
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:
itemspagination.pagepagination.page_sizepagination.total_itemspagination.total_pagessort
Write behavior
Generated writes compose shared Pharos validation, relation checks, uniqueness checks, audit logging, and state mutation.
Defaults and coercion
defaultssupplies framework-owned default valuesfield_typescoerces integers, booleans, and integer arraysauto_sequence_fieldassigns the next in-scope order value on createsoft_delete_fieldmarks records inactive instead of removing them
Validation
Generated surfaces reuse the shared form contract and validation runtime, then layer in surface-specific rules:
field_constraintsrelation_inputsuniqueness_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 today:
positive_integerdecimalemailtimedateenum
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, and presentation copy:
{
"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_titlerecords_titlecreate_titlecreate_button_labelsave_button_labelempty_state_textdelete_button_label
Admin surfaces may also declare delete_strategy:
softor omitted: mark the configuredsoft_delete_fieldinactivehard: remove the matching record from the collection entirely
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:
labelhelp_textplaceholder
Relation-input groups may override shared presentation with:
labelhelp_textempty_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:
sh pharos/scripts/pharos_runtime.sh api generate app ucal --build-dir pharos/dist --text
sh pharos/scripts/pharos_runtime.sh api schema app ucal --build-dir pharos/dist --text
sh pharos/scripts/pharos_runtime.sh admin generate app ucal --build-dir pharos/dist --text
Use these commands to inspect which generated surfaces the framework sees for an app and which schema contract a generated API will publish.
Current proof coverage
UCAL currently proves 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 proving app only wires fixtures and host dispatch into Pharos. The generated CRUD behavior itself lives in shared framework code under pharos/runtime/ and pharos/scripts/.