Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ jobs:
run: >-
uv run --with pytest --with-requirements conformance/run.py
pytest conformance/tests/ -q

- name: Site metadata and renderer tests
run: >-
uv run --with pytest --with-requirements site/requirements.txt
pytest site/tests/ -q

- name: Site filter tests
run: node --test site/tests/test_filters.cjs
1 change: 1 addition & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
- uses: actions/upload-pages-artifact@v5
with:
path: _site
retention-days: 7

deploy:
if: github.event_name == 'workflow_dispatch' && inputs.publish
Expand Down
100 changes: 100 additions & 0 deletions site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ this workflow; verify Pages availability for the repository's current plan.
A normal push only builds the artifact. It does not publish an unreviewed page.
Use the downloaded artifact or the local server to review before publication.

The `github-pages` artifact is retained for seven days; it is a downloadable
preview, not a live preview URL. Pull requests build GitHub's merge ref
(`refs/pull/<number>/merge`), not just the branch head, and never deploy.
To preview the exact source on a review branch once it is available in the
repository hosting the workflow, set the branch and the actual owner of the
family preview sites, then dispatch the build with publication disabled:

```sh
REVIEW_BRANCH='<review-branch>'
PREVIEW_OWNER='<owner-of-family-preview-sites>'
gh workflow run website.yml --ref "$REVIEW_BRANCH" -f publish=false -f family_owner="$PREVIEW_OWNER"
```

Verify the run's branch and commit, download its artifact, extract the contained
site archive, and serve it locally. A branch build does not update the live
GitHub Pages URL. Publication and repository/environment settings require
separate authorization.

## Shared identity

Use the full family name, Amplifier Smart Tools, as one masthead and footer
Expand All @@ -61,6 +79,88 @@ target checkout, review the diff, and build again. It updates only `site/theme/`
Page content stays in the owning repository. The family link registry holds only
navigation identity; the catalog's tool inventory remains `tools/*/source.json`.

## Catalog categories and recommendations

The canonical renderer supports optional catalog-owned `categories.json` and
`tools/<slug>/listing.json`, without changing the Smart Tool manifest or source-pointer
format. The registry is `{"categories":[{"id":"stable-id","label":"Display name","scope":"Kind of work covered."}]}`.
A maintainer-owned listing requires a known primary `category`; the ordinary form is `{"category": "approved-id"}`. Optional `recommended` defaults to false in a normalized copy in memory, never by mutating input or rewriting files. A provided flag must be a strict boolean. False or omission prohibits `reviewed_source`; a true designation records the selected `repository`, distribution `path`, and full source `commit`.
Entries without a listing automatically appear under Other and remain searchable by Name and Category. Other is a built-in fallback grouping, not a registry category. Without a registry, the renderer keeps legacy output and does not add category or recommendation controls.

`site/theme/catalog_metadata.py` provides the shared validators for the renderer and
catalog CI: `load_catalog_metadata(root)` returns optional categories and validated
listings, `validate_pointer` applies ref/path defaults, `read_snapshot` inspects front
matter and provenance without executing upstream code, and `recommendation_state`
compares recorded identities. Import this helper from the synced theme in catalog CI
rather than copying its rules. Invalid metadata, unsafe paths, symlinks, credential
URLs, and multiple designations in one category fail the build. A designation needing
review still occupies its category.

An effective Recommended designation requires pointer repository/ref/path to match
snapshot provenance and reviewed repository/path/commit to match that provenance.
Missing snapshots or mismatched identities show “Recommendation needs review” and
remove recommendation preference without removing the entry or its category. Recommended
means catalog maintainers selected this tool as a preferred starting point for its
category at the recorded source revision. Selection is not certification or a claim that
specification conformance or task outcomes were checked. Any review results and scope
limitations are stated only when recorded. A snapshot is point-in-time; refreshing it does
not renew the designation. Recommended is a plain, noninteractive badge. Source revision
and refresh details remain in the existing optional “Full description and source”
disclosure; the badge does not expand or link to recommendation-specific copy.

The catalog places Recommended above All tools. Each effective recommendation appears
once in Recommended as a shortcut and once in All tools as part of the complete catalog;
the two regions contain 24 card instances for 22 unique source listings. The All tools
count and global result count are based on those 22 unique listings, not the rendered
card total. The Recommended region is hidden whenever no matching effective
recommendations exist, including on the initial server-rendered page, and has no
empty-state placeholder. All tools are grouped by category by default or can be shown
in one global alphabetical Name list.

One control bar sits above all results: Search, Category, Platform, Sort by (Category or
Name), and one Clear filters action. Search, category, and platform filter both copies;
sorting changes only All tools, and clearing filters preserves the selected sort. Category
scopes remain registry metadata and are not inserted into the control bar or displayed
when a category is selected. The Other option and group appear only when at least one current
entry lacks classification. Native select controls keep their dimensions as values change. At
phone widths, Search spans the bar, Category and Platform share a two-column row, and Sort
by and Clear filters share the next row. Category labels remain visible on every card and
searchable with displayed tool names.

The all-results count is a live status for unique All tools listings; Recommended and All
tools have separate counts. When no listing matches, one global empty message replaces
section-specific empty messages. Without JavaScript, the filter bar and Name list stay
hidden, all 22 unique listings remain visible in All tools along with the two featured
shortcuts, and a zero-card Recommended region is hidden. Ordinary category group headings
are noninteractive, with no second category-navigation area.

With a category registry, every card names its category. Recommended shortcuts use the
section heading instead of a redundant “Recommended” badge; their All tools copies show
that badge. Stale designations retain “Recommendation needs review”; ordinary cards have
no recommendation-status badge. Cards without a listing show “Category: Other.” Even without snapshots or JavaScript, these entries remain readable. A legacy catalog without a registry keeps its existing card markup.

Tool creators do not nominate or select entries; catalog maintainers make those
decisions. Submissions are source-only and category-agnostic: submitters do not add, choose, or create categories or recommendations and need not know category definitions. Maintainers assign classification from the approved registry in separate metadata, never upstream manifests or source pointers. Category additions, label changes, and scope changes require their separate review and approval.

### Maintainer recommendation review

The normative maintainer roles and designation, renewal, replacement, and withdrawal
procedures live in the catalog's
[maintainer curation guide](https://github.com/microsoft/amplifier-smart-tools-catalog/blob/main/docs/maintainers.md).

`site/sync_theme.py` copies the helper with the renderer, CSS, JavaScript, family registry, license, and shared motion assets. Review the synchronized diff and run a build in the target catalog before adoption; do not update only a vendored theme copy.

## Site tests

Run deterministic Python renderer/metadata tests and the JavaScript filter harness from the repository root:

```sh
uv run --with pytest --with-requirements site/requirements.txt pytest site/tests/ -q
node --test site/tests/test_filters.cjs
```

The Python suite also checks legacy rendering, emitted assets, and theme sync. The Node harness executes the production script against a minimal DOM; it is not a browser layout or supported-host trial. Run browser and read-only host discovery scenarios separately before making those claims. CI runs these checks alongside the existing conformance kit tests.

## Motion assets

The shared title mark uses an eight-second looping GIF with a static PNG fallback.
Expand Down
1 change: 1 addition & 0 deletions site/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
markdown-it-py==4.0.0
PyYAML==6.0.3
2 changes: 1 addition & 1 deletion site/sync_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
target = root/'site/theme'
if target == source:
continue
for name in ('build.py', 'style.css', 'site.js', 'family.json', 'LICENSE'):
for name in ('build.py', 'catalog_metadata.py', 'style.css', 'site.js', 'family.json', 'LICENSE'):
target.mkdir(exist_ok=True)
shutil.copy2(source/name, target/name)
if (source/'assets').is_dir():
Expand Down
Loading
Loading