diff --git a/docs/classification-strategy.md b/docs/classification-strategy.md
new file mode 100644
index 0000000..362be62
--- /dev/null
+++ b/docs/classification-strategy.md
@@ -0,0 +1,43 @@
+# Classification Strategy
+
+Repo-registry needs classification for orientation without pretending repository
+behavior is always cleanly separable. The review UI should therefore support two
+layers of classification:
+
+- `primary_class`: the main class-defining attribute used for grouping, filtering,
+ and first-glance orientation.
+- `attributes`: additional labels that can overlap, qualify, or challenge the
+ primary class.
+
+Observed facts already follow this shape informally: `kind` is the primary class
+(`interface`, `framework`, `test`, `documentation`, and so on), while path, name,
+value, and metadata provide secondary attributes.
+
+For approved and candidate graph elements, the target model is:
+
+- Ability primary classes describe the repository's core domain or operational
+ purpose, such as `repository-intelligence`, `workflow-automation`, `content-
+ publishing`, or `developer-tooling`.
+- Capability primary classes describe the kind of behavior exposed, such as
+ `ingestion`, `analysis`, `review`, `search`, `export`, `coordination`, or
+ `deployment`.
+- Feature primary classes describe the delivery surface or user-visible feature
+ family, such as `business-usecase`, `ui`, `api`, `cli`, `backend`, `storage`,
+ `integration`, or `diagnostic`.
+
+Features are the most fluid layer. A single feature can be both `ui` and `review`,
+or both `api` and `ingestion`. The primary class should answer "where should a
+curator first look for this?" Secondary attributes should preserve the overlap:
+surface (`ui`, `api`, `cli`), intent (`review`, `search`, `diagnostic`), and
+domain (`repository-intelligence`, `capability-mapping`) can all be true at once.
+
+Current implementation status:
+
+- Facts can be searched and filtered by `kind` in the element listing UI.
+- Features can be searched and filtered by their existing single `type`.
+- Abilities and capabilities currently use placeholder classes (`ability`,
+ `capability`) until the schema gains `primary_class` and `attributes`.
+
+Next schema step: add `primary_class` plus a JSON/list `attributes` field to
+candidate and approved abilities, capabilities, and features. Candidate generation
+can then propose classifications, while review keeps the right to correct them.
diff --git a/src/repo_registry/web_ui/views.py b/src/repo_registry/web_ui/views.py
index 8ec2dc1..6f0492b 100644
--- a/src/repo_registry/web_ui/views.py
+++ b/src/repo_registry/web_ui/views.py
@@ -936,6 +936,8 @@ def repository_element_listing(
repository_id: int,
scope: str = Query("approved"),
type: str = Query("abilities"),
+ q: str = Query(""),
+ class_filter: str = Query(""),
analysis_run_id: int | None = Query(default=None),
service: RegistryService = Depends(get_service),
) -> HTMLResponse:
@@ -943,33 +945,53 @@ def repository_element_listing(
title = element_listing_title(repository.name, scope, type)
if scope == "approved":
graph = asdict(service.ability_map(repository_id))
- rows = render_graph_element_rows(graph, type)
+ elements = graph_element_rows(graph, type)
elif scope == "candidate":
if analysis_run_id is None:
runs = service.list_analysis_runs(repository_id)
latest = latest_completed_candidate_graph(service, repository_id, runs)
if latest is None:
- rows = '