Route Directory Contract

Reserved files, inheritance rules, route kinds, and shell contract for filesystem routes

2 min read 487 words

Overview

The route directory contract defines how Chirp discovers and wires filesystem routes. It specifies reserved files, their scope (inherited vs route-local), and how metadata, context, and layouts combine. Understanding this contract helps you structure route directories correctly and avoid common mistakes.

Reserved Files

File Scope Purpose
page.py route-local Primary route handler. Exportsget, post, etc. or handler. page.py → directory URL; other .pyfiles append their stem.
page.html route-local Primary page template. Sibling ofpage.py. Defines fragment blocks.
_meta.py route-local Route metadata (title, section, breadcrumb_label, shell_mode). ExportsMETA or meta().
_context.py inherited Subtree-scoped context provider. Exportscontext()receiving path params, parent context, and services.
_layout.html inherited Subtree layout wrapper. Declares{# target: element_id #} and {% block content %}.
_actions.py route-local Mutation handlers. Exports@actiondecorated functions.
_viewmodel.py route-local View assembly. Exportsviewmodel()merging data for templates.

RouteMeta

_meta.py provides route metadata via a static META constant or a meta()callable:

from chirp.pages.types import RouteMeta

META = RouteMeta(
    title="Skills",
    section="discover",
    breadcrumb_label="Skills",
    shell_mode="tabbed",
)

Or dynamically:

def meta(name: str) -> RouteMeta:
    return RouteMeta(title=f"Skill: {name}", breadcrumb_label=name)

Fields: title, section, breadcrumb_label, shell_mode, auth, cache, tags.

Sections

Register sections beforemount_pages():

from chirp.pages.types import Section, TabItem

app.register_section(Section(
    id="discover",
    label="Discover",
    tab_items=(TabItem(label="Skills", href="/skills"),),
    breadcrumb_prefix=({"label": "App", "href": "/"},),
))

Routes bind to sections via RouteMeta.section. The framework resolves tab_items and breadcrumb_prefixfrom the matched section.

Context Cascade

_context.py providers run root-first. Each receives path params, accumulated parent context, and service providers. Child output overrides parent. shell_actionsmerges deeply.

Layout Chain

Layouts inherit down the directory tree. Each_layout.html declares {# target: element_id #}. Render depth depends on HX-Target: full page renders all; fragment requests start at the matching layout.

Shell Context Assembly

The framework provides:page_title, breadcrumb_items, tab_items, current_path. Resolution order: RouteMeta→ section → handler override.

Route Kinds

Kind Files Description
page page.py, page.html Standard page with template
detail page.py, page.html in{param}/ Parametrized page
action page.py (no template) Mutation-only route
redirect page.py returning Redirect Redirect route

Actions

_actions.py exports @action decorated handlers. Forms use _actionfield to dispatch. The framework discovers actions at route registration.

Viewmodel

_viewmodel.py exports viewmodel()for complex view assembly. Its output merges after cascade and shell context.

Contract Validation

app.check()validates route contracts: section bindings, shell mode/block alignment, route file consistency, duplicate routes, section tab hrefs, and context provider signatures.

Introspection

Whenconfig.debug=True:

  • Debug headers:X-Chirp-Route-Kind, X-Chirp-Route-Files, X-Chirp-Route-Meta, X-Chirp-Route-Section, X-Chirp-Context-Chain, X-Chirp-Shell-Context
  • Route explorer:GET /__chirp/routesshows the full route tree with drill-down
  • HTMX panel: Activity log entries show route metadata when expanded