AI Assistant Instructions¶
pydantic-schemaforms ships packaged, verified integration instructions for AI coding
assistants (Claude, GitHub Copilot, and a generic profile), so an assistant working in an
app repo that depends on this library can produce correct, idiomatic integrations without
reverse-engineering package internals or guessing at deprecated patterns.
Two things make this different from a normal docs page copied into a prompt:
- It ships with the library. Every install of
pydantic-schemaformscarries the current instructions as packaged data — they can't drift out of sync the way a copy-pasted snippet in someone'sCLAUDE.mdcan. - It's discoverable without being asked. The top-level package docstring and
FormModel's class docstring both point an AI assistant at this feature, so it can find it on its own the first time it inspects the library — seepydantic_schemaforms/__init__.py.
What's covered¶
Each profile documents the same underlying integration contract:
- A complete, verified worked example: a
FormModel+ FastAPI GET/POST route pair that renders, re-renders with errors on invalid submission, and succeeds on valid submission — checked end-to-end with aTestClient, not just read from source. - An authoritative table of every valid
ui_elementstring (grouped by category, aliases included), so an assistant never has to guess at or invent a plausible-sounding widget name. Each profile calls out explicitly that an unrecognizedui_elementdoes not raise an error — it silently falls back to a plain text input. - The
FormModel+Field()+render_form_html()pattern, and the lower-boilerplateFormModel.validate()/render_with_errors()alternative. - CSRF protection (
csrf_mode,csrf_token_provider, verification before validation). - Repeating sub-forms (
model_list): resolving the item model from alist[ItemModel]annotation, and theui_add_button_label/ui_item_title_template/ui_collapsible_items/ui_items_expandedcustomization knobs. - Dual-use JSON + HTML models via
as_api_model(). - The distinction between Field constraints (
min_length,max_length,pattern,ge/le— enforced server-side and reflected in HTML automatically) andui_options(UI-only knobs with no constraint equivalent) — getting this wrong produces a form that looks validated but isn't. - The distinction between
ui_element(which widget renders) and the field's Pydantic type (what's actually validated) — e.g.ui_element="email"only picks the HTML5 email input;EmailStris what makes the address format actually enforced on.validate(). - Deterministic field-mapping rules (Python type →
ui_element) so repeated runs converge on the same output instead of drifting between requests.
HTMX live validation (LiveValidator) is intentionally not yet part of the packaged
instructions — see Validation Guide for that.
Python API¶
from pydantic_schemaforms import (
available_instruction_profiles,
get_app_instructions,
suggested_instruction_filename,
)
available_instruction_profiles()
# ('claude', 'copilot', 'generic')
suggested_instruction_filename('claude')
# 'CLAUDE.md'
suggested_instruction_filename('copilot')
# '.github/copilot-instructions.md'
suggested_instruction_filename('generic')
# 'AI_INSTRUCTIONS.md'
get_app_instructions('claude')
# full instructions text for the Claude profile
Aliases are accepted where they're unambiguous — get_app_instructions('github-copilot')
and get_app_instructions('anthropic-claude') both resolve to the same profile as
'copilot' / 'claude'.
An unsupported profile raises ValueError naming the supported profiles, rather than
silently falling back to one.
CLI¶
Bootstrap an app repo's instruction file in one command instead of writing a throwaway script:
python -m pydantic_schemaforms.ai_instructions claude --write
# writes ./CLAUDE.md
python -m pydantic_schemaforms.ai_instructions copilot --write
# writes ./.github/copilot-instructions.md (parent dirs created as needed)
python -m pydantic_schemaforms.ai_instructions generic > AI_INSTRUCTIONS.md
# or redirect stdout yourself
Full CLI usage:
usage: python -m pydantic_schemaforms.ai_instructions [-h] [--write]
[--output PATH]
[profile]
positional arguments:
profile Instruction profile (aliases accepted). One of: claude,
copilot, generic.
options:
-h, --help show this help message and exit
--write Write to the suggested destination file (e.g. CLAUDE.md)
instead of stdout.
--output PATH Write to an explicit path instead of stdout or the suggested
destination.
Try it in the example app¶
The FastAPI example app renders all three profiles as tabs at /ai-instructions (linked
from the "AI Assistant Instructions" card on the home page) — the same content
get_app_instructions() returns, rendered from the packaged markdown files.
Authoring the packaged instructions¶
The 3 files under pydantic_schemaforms/assets/ai/ (generic_app_instructions.md,
copilot_app_instructions.md, claude_app_instructions.md) are generated — never
hand-edit them directly, the same rule CLAUDE.md documents for docs/index.md etc. Edit
pydantic_schemaforms/assets/ai/_shared_instructions.md (the ~90% common body — contract,
library boundary, worked example, ui_element table, field cookbook, mapping, layout, CSRF,
model_list, live validation, common mistakes) or _profile_{generic,copilot,claude}.md (the
small per-profile title/intro and closing prompt-starter, containing an
<!-- INCLUDE: _shared_instructions.md --> marker) instead, then run make ai-instructions
(scripts/build_ai_instructions.py) to regenerate the 3 packaged files. This also runs as part
of make create-docs/create-docs-local.
Keeping this feature honest¶
The packaged instructions are cross-checked against the library's actual behavior, not
just written once and left to drift — every claim in them (which ui_* kwargs exist,
what model_list options do, how CSRF verification works) has been verified by running
the corresponding code, not just read from source. If you add a Field kwarg or change
model_list behavior, update pydantic_schemaforms/assets/ai/_shared_instructions.md (or
the relevant _profile_*.md) and re-run make ai-instructions in the same change.
The "Supported ui_element values (authoritative)" table in each profile doc is checked
automatically: tests/test_ai_instructions.py::test_authoritative_ui_element_table_matches_registry
parses every backticked token out of that section and asserts it exists in
get_input_component_map(). If you add a new input class, give it its own ui_element
(see tests/test_input_registry.py — the registry rejects two classes silently sharing
one ui_element, which previously caused ui_element="hidden"/"date"/"number"/"range"
to resolve to the wrong widget) and add it to the three doc tables in the same change, or
this test will fail.