From 1724d7f722577f5d9a71fe42c0bd63989e59dc0f Mon Sep 17 00:00:00 2001 From: David Amos Date: Thu, 24 Sep 2026 09:51:16 -0500 Subject: [PATCH 01/13] Add template authoring contract and validation What changed: - Document the model-package, README, and enhanced sidecar authoring workflow. - Add a runnable sample template, focused validation tests, and CI checks for new v1 sidecars. Why: - Give contributors a consistent way to create or update generated template documentation while preserving README-only templates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .agents/skills/template-authoring/SKILL.md | 99 ++++++++ .../resources/guide-links.md | 48 ++++ .../resources/template-docs.md | 167 +++++++++++++ .../resources/validation.md | 33 +++ .../prompts/cleanup-template-code.prompt.md | 183 ++------------ .../prompts/create-template-readme.prompt.md | 121 +++------ .github/prompts/review-template.prompt.md | 136 +++------- .../prompts/update-template-readme.prompt.md | 90 ++----- .github/workflows/lint.yml | 18 +- .pre-commit-config.yaml | 2 +- CONTRIBUTING.md | 111 +++++++-- sample-template/README.md | 232 ++++++------------ sample-template/model/__init__.py | 6 + sample-template/model/schema.py | 8 + sample-template/model/source.py | 11 + sample-template/template-docs.yaml | 73 ++++++ sample-template/template.py | 22 +- scripts/validate_changed_templates.py | 172 +++++++++++++ tests/test_validate_changed_templates.py | 65 +++++ 19 files changed, 989 insertions(+), 608 deletions(-) create mode 100644 .agents/skills/template-authoring/SKILL.md create mode 100644 .agents/skills/template-authoring/resources/guide-links.md create mode 100644 .agents/skills/template-authoring/resources/template-docs.md create mode 100644 .agents/skills/template-authoring/resources/validation.md create mode 100644 sample-template/model/__init__.py create mode 100644 sample-template/model/schema.py create mode 100644 sample-template/model/source.py create mode 100644 sample-template/template-docs.yaml create mode 100644 scripts/validate_changed_templates.py create mode 100644 tests/test_validate_changed_templates.py diff --git a/.agents/skills/template-authoring/SKILL.md b/.agents/skills/template-authoring/SKILL.md new file mode 100644 index 00000000..a3dc3b57 --- /dev/null +++ b/.agents/skills/template-authoring/SKILL.md @@ -0,0 +1,99 @@ +--- +name: template-authoring +description: Create, update, or review RelationalAI templates, including the model package, README, template-docs sidecar, runbook, and validation. +--- + +# Template authoring + +Use this skill for any request to create, refactor, document, or review a +template in this repository. + +## Select the operation + +- **Create**: start from `sample-template/`, use the v1 layout, and complete + every required artifact. +- **Update**: preserve the existing command, behavior, and outputs while + keeping code, README, runbook, and sidecar synchronized. +- **Review**: do not edit. Report reproducibility, structure, documentation, + sidecar, and validation issues with exact file paths. + +Legacy templates without `template-docs.yaml` may remain README-only. Do not +require migration merely because an existing template changed. New +`v1//README.md` files require a sidecar, and an adopted sidecar must not +be removed while the template remains. + +## Required workflow + +1. Read the entire template directory, including its runner, `pyproject.toml`, + README, runbook, data files, and existing sidecar. +1. Preserve the entrypoint, solver choices, constraints, thresholds, output + labels, and data semantics unless the request explicitly changes them. +1. For new v1 templates and intentional migrations, use this layout: + + ```text + v1// + ├── README.md + ├── template-docs.yaml + ├── model/ + │ ├── __init__.py + │ ├── schema.py + │ └── source.py + ├── .py + ├── runbook.md + ├── pyproject.toml + └── data/ + ``` + +1. Put only stable model declarations in `model/schema.py`: create the + `Model`, then declare concepts, identity keys, properties, and + relationships with direct `model.Concept`, `model.Property`, and + `model.Relationship` calls. +1. Put source reads, normalization, and base-fact mappings in + `model/source.py`. Keep solver declarations, derived reasoning, result + queries, and reporting in the runner. +1. Re-export the model and stable schema symbols from `model/__init__.py`. + A single-source template may load its source on import. A multi-source + template must expose explicit loader functions and let each runner choose + the correct one. +1. Keep README commands, code links, `runbook.md`, and `template-docs.yaml` + aligned with the final paths and symbols. +1. Follow [the sidecar contract](./resources/template-docs.md) and select only + routes from [the guide catalog](./resources/guide-links.md). +1. Run [focused validation](./resources/validation.md). Do not claim runtime + verification if credentials, an engine, Snowflake, or other connected + resources were unavailable. + +## README contract + +The README remains the runnable GitHub entry point and owns the title, +description, problem statement, prerequisites, commands, and expected output. +README-only templates can retain customization, troubleshooting, and support +information. An enhanced template can keep a short README when its docs page +provides the model explorer and walkthrough. + +- Explain the problem before implementation details. +- For a long-form README, put the solution flow in **What you'll build** and + use **What's included** to identify the schema, source, runner, runbook, + sample data, and outputs. +- When the sidecar sets `page.download_and_run_from_readme: true`, a README may + contain only **What this template is for** and **Quickstart**, plus an + opening link to the docs page. Put access requirements, pinned dependencies, + commands, results, and necessary data attribution in Quickstart. Set + `page.omit_readme_preamble: true` when that opening link is for README + readers only. +- Do not add a standalone **Template structure** section. The generated docs + file browser already serves that purpose. +- For long-form READMEs, use small Markdown tables for representative sample + rows. Keep large data sets out of the README. +- If **Model overview** or **How it works** remain in the README, keep them + concise; enhanced docs can omit them through `page.omit_from_readme`. +- Keep the Quickstart copy/paste-friendly and include a small success signal. + +## Completion + +Report: + +- Files changed. +- Behavior intentionally preserved or changed. +- Checks run and their exact outcomes. +- Any connected runtime command that remains unverified and why. diff --git a/.agents/skills/template-authoring/resources/guide-links.md b/.agents/skills/template-authoring/resources/guide-links.md new file mode 100644 index 00000000..e196ca9b --- /dev/null +++ b/.agents/skills/template-authoring/resources/guide-links.md @@ -0,0 +1,48 @@ +# Public guide-link catalog + +Use only public, non-draft RelationalAI documentation routes. Prefer the one or +two guides that directly support each walkthrough step. + +## Modeling + +- `/build/guides/modeling/create-model-instance/` +- `/build/guides/modeling/declare-concepts/` +- `/build/guides/modeling/declare-relationships-and-properties/` +- `/build/guides/modeling/declare-data-sources/` +- `/build/guides/modeling/define-base-facts/` +- `/build/guides/modeling/derive-facts-with-logic/` +- `/build/guides/modeling/query-a-model/` + +## Rules-based reasoning + +- `/build/guides/reasoning/rules-based/` +- `/build/guides/reasoning/rules-based/aggregate-and-group-data/` +- `/build/guides/reasoning/rules-based/handle-missing-data/` +- `/build/guides/reasoning/rules-based/use-dates-and-datetimes/` +- `/build/guides/reasoning/rules-based/work-with-numbers/` +- `/build/guides/reasoning/rules-based/work-with-strings/` + +## Graph reasoning + +- `/build/guides/reasoning/graph/create-a-graph/` +- `/build/guides/reasoning/graph/run-an-algorithm/` + +## Predictive reasoning + +- `/build/guides/reasoning/predictive/construct-a-graph/` +- `/build/guides/reasoning/predictive/define-a-learning-task/` +- `/build/guides/reasoning/predictive/solve-a-classification-problem/` +- `/build/guides/reasoning/predictive/inspect-predictions/` +- `/build/guides/reasoning/predictive/monitor-training/` + +## Prescriptive reasoning + +- `/build/guides/reasoning/prescriptive/decision-problems/create/` +- `/build/guides/reasoning/prescriptive/decision-problems/variables/` +- `/build/guides/reasoning/prescriptive/decision-problems/constraints/` +- `/build/guides/reasoning/prescriptive/decision-problems/solve/` +- `/build/guides/reasoning/prescriptive/decision-problems/solutions/` + +The docs preview validates routes against the current public collection and +rejects missing, private, or draft targets. Update this catalog when a route +moves; do not work around validation with a redirect, query, or fragment. diff --git a/.agents/skills/template-authoring/resources/template-docs.md b/.agents/skills/template-authoring/resources/template-docs.md new file mode 100644 index 00000000..1c01dac2 --- /dev/null +++ b/.agents/skills/template-authoring/resources/template-docs.md @@ -0,0 +1,167 @@ +# `template-docs.yaml` contract + +The sidecar is inert data consumed by the documentation build. It must contain +no HTML, MDX, imports, exports, expressions, JavaScript, or executable code. +Unknown keys are errors. + +## Top-level shape + +```yaml +version: 1 + +page: + lead_from_readme: [] + omit_from_readme: [] + +model_explorer: + title: Plain-text title + description: Plain-text description + concepts: [] + relationships: [] + sources: [] + +guided_walkthrough: + title: Plain-text title + description: Plain-text description + steps: [] + +sample_data: + default_rows: 4 + tables: [] +``` + +`sample_data` is optional. Every other top-level key is required. + +## Page composition + +- `lead_from_readme`: zero or more unique lowercase README heading IDs. +- `omit_from_readme`: zero or more unique lowercase README heading IDs. +- A heading ID cannot appear in both lists. +- Both lists must reference headings still present in the README. Use + `omit_from_readme: []` when a shorter README removes those sections. +- Use the IDs produced by GitHub-style heading slugs, such as + `what-this-template-is-for` and `what-youll-build`. +- `download_and_run_from_readme: true` (optional) extracts the README Quickstart + into a generated "Download and run the template" section. State access and + Python requirements before the first step, include any required SQL setup + there, then provide five ordered steps: download, Python environment, + install, configure with the Start building with PyRel configuration builder, + and run. Keep any representative output directly after its runner command. + Templates without this flag retain their existing rendering. +- `omit_readme_preamble: true` (optional) excludes the text before the first + README `##` heading from the enhanced HTML and Markdown pages. Use it when + the README opens with links to its own generated model explorer and + walkthrough. Without the flag, existing preamble text remains visible. + +## Model explorer + +`concepts` contains 1-50 entries: + +```yaml +- symbol: Account + description: A customer account evaluated by the model. +``` + +The symbol must be a top-level concept declared in `model/schema.py`. Identity +keys and properties are extracted from Python and must not be repeated. + +`relationships` contains up to 100 entries: + +```yaml +- symbol: Transaction.sender + label: sent by + reading: Transaction is sent by Account + description: Connects each transfer to its sending account. +``` + +The relationship symbol must resolve in `model/schema.py`. Endpoints are +extracted; the sidecar supplies the reader-facing label, reading, and +description. + +`sources` contains up to 50 entries: + +```yaml +- path: data/accounts.csv + description: Account identifiers and attributes. +``` + +Use repository-relative paths. The extractor infers mappings from +`model/source.py`. Add `populates` only when dynamic code makes the mapping +ambiguous: + +```yaml + populates: + - Account + - Transaction.sender +``` + +Every override must name a concept or relationship selected by +`model_explorer`. + +## Guided walkthrough + +Provide 1-6 semantic steps. One or two steps may be present while a walkthrough +is under editorial review; a complete walkthrough should normally have 3-6 +steps. Each step has one canonical Python file, one focus form, and 1-2 public +guide links: + +```yaml +- id: load-inputs + title: Load account records + explanation: >- + load_accounts maps account rows into the shared Account concept. + focus: + file: model/source.py + symbols: + - load_accounts + references: + load_accounts: + symbol: load_accounts + Account: + symbol: Account + links: + - title: Define base facts + href: /build/guides/modeling/define-base-facts/ +``` + +- Prefer `focus.symbols`; use 1-25 unique Python symbols. +- Use `focus.anchors` only for a meaningful span without a stable symbol: + + ```yaml + focus: + file: runner.py + anchors: + - start: "# Stage 3: Optimize" + end: "# Stage 4: Report" + ``` + +- Anchor text must occur exactly once, in order, in the selected file. +- Reference keys must appear verbatim in the explanation. +- Reference values contain only `symbol`. +- IDs use lowercase words separated by hyphens. +- Update the sidecar whenever a referenced path, symbol, or anchor changes. + +## Sample data + +Select only local CSV files and representative columns: + +```yaml +sample_data: + default_rows: 4 + tables: + - path: data/accounts.csv + title: Accounts + description: Representative account records. + columns: + - id + - status + rows: 3 +``` + +- `default_rows` and per-table `rows` must be integers from 1 through 10. +- A table needs at least one unique column and may select at most 50. +- The docs build reads headers, counts all rows, and emits only the bounded + sample. Do not copy data rows into YAML. +- Order tables by the reader's workflow rather than alphabetically. + +Use `sample-template/template-docs.yaml` as the copyable minimal example. diff --git a/.agents/skills/template-authoring/resources/validation.md b/.agents/skills/template-authoring/resources/validation.md new file mode 100644 index 00000000..baed343c --- /dev/null +++ b/.agents/skills/template-authoring/resources/validation.md @@ -0,0 +1,33 @@ +# Focused validation + +Run the smallest checks that cover the changed template and repository policy. +From the repository root: + +```bash +ruff check sample-template scripts tests +python -m compileall -q sample-template +python -m unittest tests/test_validate_changed_templates.py +python scripts/generate_version_indexes.py --check +``` + +For a committed branch, run the pull-request policy against its base: + +```bash +python scripts/validate_changed_templates.py --base-ref origin/main +``` + +The changed-template check: + +- Requires `template-docs.yaml` when a `v1//README.md` is newly added or + moved into a new template directory. +- Allows changes to existing README-only templates without forcing migration. +- Rejects removing an adopted sidecar while that template's README remains. + +For any new or changed sidecar, use the docs preview workflow as the +authoritative strict-schema, source-symbol, anchor, CSV, and public-guide-link +validation. + +Finally, run the exact README command in a fresh environment. If it needs +customer credentials, a RelationalAI engine, Snowflake, or another unavailable +connected resource, report the command as unverified. Linting and compilation +are not runtime proof. diff --git a/.github/prompts/cleanup-template-code.prompt.md b/.github/prompts/cleanup-template-code.prompt.md index a09faf5e..8f9b50ce 100644 --- a/.github/prompts/cleanup-template-code.prompt.md +++ b/.github/prompts/cleanup-template-code.prompt.md @@ -1,170 +1,33 @@ --- name: cleanup-template-code -description: Reorganize a RelationalAI template script to match the standard structure and comments used by other templates, without changing functionality. +description: Refactor a RelationalAI template into the standard model package without changing behavior. +inputs: + version: + description: Template version folder. + default: v1 + templateName: + description: Template folder name. --- -# Template Code Cleanup for RelationalAI Templates +# Refactor template code -## Configuration Options - -VERSION=${{input:version:v0.13}} +VERSION=${{input:version:v1}} TEMPLATE_NAME=${{input:templateName}} -## Role - -You are an expert Python engineer and technical writer. You specialize in producing clean, consistent, educational template scripts for RelationalAI. - -You MUST preserve functionality. Your changes are limited to: - -- Rearranging code (including moving blocks between sections) -- Adding, removing, or editing comments and docstrings -- Minor formatting normalization (blank lines, line wrapping, consistent naming) - -You MUST NOT: - -- Change logic, constraints, objectives, solver choices, scenario values, filtering thresholds, outputs, or data semantics -- Add new features, flags, optional behaviors, or dependencies -- Rename concepts/properties/columns in a way that changes results - -## Task - -Given a template located at `${VERSION}/${TEMPLATE_NAME}/`: - -If no `version` input is provided, default to `v0.13`. - -1. Review the entire template folder, focusing on the main script `${TEMPLATE_NAME}.py` (or the template’s actual entrypoint). -2. Update the script so it matches the organization, formatting, and commenting style used by the canonical templates (for example: `diet.py`, `ad_spend_allocation.py`, `factory_production.py`). -3. Ensure the script still runs the same way and prints the same shape of output. - -## Standard script structure - -Reformat the script to follow this high-level structure (use these headings verbatim unless the template truly requires different phrasing): - -1. Module docstring - - Start with a short title line: `