[osprey-ui] Rules Registry page#277
Open
haileyok wants to merge 15 commits into
Open
Conversation
Rename _features_ast_utils.py to _engine_ast_utils.py and move collect_name_references (formerly _collect_name_references) from features.py into it. Both features.py and the upcoming rules.py import the three helpers from one place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After moving _collect_name_references to _engine_ast_utils, the imports for Name and List as AstList are no longer used. Remove them to fix ruff F401 violations and pass linting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds /rules endpoint gated by CanViewDocs. _extract_rules_from_engine walks every source twice: sub-pass 1 builds a whenrules_ref_count map (WhenRules can textually precede the Rule it references); sub-pass 2 collects Rule nodes and attaches when_all conditions, the description template, referenced feature names, and the WhenRules reference count. Not yet registered in app.py — that follows in the next commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mounts the rules blueprint via _register_with_prefix next to the existing features registration, so /rules and /api/rules both resolve to the same handler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers: empty engine returns zero-counts, response shape matches RulesListResponse, referenced_features unions names from when_all and FormatString descriptions, unused_total correctness, dual-route equality, 401 for users without CAN_VIEW_DOCS, and the back-reference regression where WhenRules textually precedes the Rule it names (justifies the two-sub-pass walk). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the data layer for the Rules Registry page: RuleInfo and RulesListResponse types mirroring the backend, a getRulesList action using HTTPUtils, and the /rules route constant. Page component lands in the next commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thin RulesPage shell uses usePromiseResult + renderFromPromiseResult.
RulesPageContent owns filter + pagination state in a single
useReducer<FiltersState, FiltersAction> — every filter change
atomically resets page: 1, so no cross-effect setPage useEffect is
needed. RuleDetail uses the Antd 5 styles={{ label: ... }} API
instead of the deprecated labelStyle.
Route + NavBar wiring follow in the next commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds /rules route to App.tsx and a Manage-section NavBar entry
('Rules', FileTextOutlined) so the Rules Registry page is reachable
from the sidebar and via direct URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The label column was auto-sizing to "Referenced features (N)" with no breathing room before the value, so it visually butted up against the chips. Bump label width to 180px and add 16px right padding so every row has consistent gutter between label and value. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Member
Author
Run prettier to fix line-length wrapping and ternary formatting that slipped through Phase 6 — RulesPage.tsx hadn't been formatted before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original test placed a WhenRules block textually before the Rule it
referenced within main.sml. The engine validator forbids forward
references within a file ('unknown identifier'), so the fixture failed
at setup before the endpoint was ever exercised — CI caught this.
The two-sub-pass walk's actual justification is cross-source iteration
order: main.sml is iterated first (dict insertion order), and its
WhenRules can reference a Rule defined in an imported source iterated
second. A single-pass walk misses that reference because the Rule
hasn't been recorded yet. Move the Rule into extra_rules.sml,
Import it from main.sml, and rename the test accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rules.py: merge the two-sub-pass walk into one. WhenRules counts are keyed by Rule name (order-independent), so a single AST traversal that accumulates counts and collects Rule entries, plus a final backfill loop over rules, gives the same result with one fewer pass over every statement of every source. Matches the pattern features.py already uses. RulesPage.tsx: drop the redundant "0 — unused" branch in RuleDetail — the "unused" tag is already shown in RuleHeader for the same case. Trim the reducer comment to the actual invariant (no PR context). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
haileyok
commented
May 20, 2026
Per PR review — flatter usages below the top-of-function destructure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new “Rules Registry” view to the upstream UI/API, analogous to the existing Features Registry, exposing a catalog of Rule(...) definitions and their usage from WhenRules(...) blocks.
Changes:
- Backend: adds a
/rules(and/api/rules) endpoint gated byCanViewDocs, plus shared AST utilities for name-reference collection. - Backend tests: introduces a dedicated
test_rules.pysuite covering response shape, FormatString descriptions, reference counting, dual-route registration, and auth gating. - Frontend: adds a new
/rulesroute and page with filtering/sorting/pagination, types/actions wiring, and a NavBar entry.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
osprey_worker/src/osprey/worker/ui_api/osprey/views/tests/test_rules.py |
Adds coverage for the new rules registry endpoint (shape, refs, auth, route aliases). |
osprey_worker/src/osprey/worker/ui_api/osprey/views/rules.py |
Implements the Rules API blueprint and extraction logic from the engine AST. |
osprey_worker/src/osprey/worker/ui_api/osprey/views/features.py |
Switches to shared AST helper (collect_name_references) after util consolidation. |
osprey_worker/src/osprey/worker/ui_api/osprey/views/_engine_ast_utils.py |
Generalizes AST helper module and exposes collect_name_references. |
osprey_worker/src/osprey/worker/ui_api/osprey/app.py |
Registers the new rules blueprint (root + /api prefix). |
osprey_ui/src/types/RulesTypes.tsx |
Adds frontend types for rules list payload and sorting. |
osprey_ui/src/Constants.tsx |
Adds Routes.RULES. |
osprey_ui/src/components/rules/RulesPage.tsx |
Implements the Rules Registry UI (filters, sorting, pagination, details view). |
osprey_ui/src/components/rules/RulesPage.module.css |
Styles for the new Rules page. |
osprey_ui/src/components/navigation/NavBar.tsx |
Adds “Rules” entry to the Manage section. |
osprey_ui/src/App.tsx |
Registers the /rules route to render RulesPage. |
osprey_ui/src/actions/RulesActions.tsx |
Adds getRulesList API call wrapper. |
CHANGELOG.md |
Notes the new Rules Registry page addition. |
Member
|
Feedback from an adopter: Can we add a visual chart on the bottom of each rule to see when / how often it triggers? |
Member
Author
this should be relatively easy to do i think...the data is already in druid so |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
Migrating another internal page to upstream. This one complements the features registry by displaying all of the rules that exist within Osprey.
Changes Made
Backend (
osprey_worker):_features_ast_utils.py→_engine_ast_utils.pyand promoted_collect_name_referencesto a publiccollect_name_referenceshelper now shared byfeatures.pyandrules.py.views/rules.pyFlask blueprint:_build_whenrules_ref_count(sub-pass 1, counts WhenRules → Rule refs),_extract_rules_from_engine(sub-pass 2, collects Rule nodes), andrules_listroute gated by@require_ability(CanViewDocs).app.pyso both/rulesand/api/rulesresolve.tests/test_rules.pywith 7 tests covering empty engine, response shape, FormatString descriptions, back-reference regression,unused_totalcorrectness, dual-route equality, and 401 gating.Frontend (
osprey_ui):types/RulesTypes.tsx(RuleInfo,RulesListResponse,SortKey),actions/RulesActions.tsx(getRulesList), andRoutes.RULES = '/rules'.components/rules/RulesPage.tsx— five components in post-nit FeaturesPage form: thin shell usesusePromiseResult+renderFromPromiseResult;RulesPageContentowns a singleuseReducer<FiltersState, FiltersAction>for filters and pagination, with every filter action atomically resettingpage: 1(no cross-effectuseEffectneeded);RuleDetailuses the Antd 5Descriptions styles={{ label: ... }}API.FileTextOutlined, label "Rules") wired in.Confidence Level
Confidence Level: Claude
Testing
Static / type checks (passed in-session):
python3 -c "import ast; ast.parse(...)"on all touched.pyfiles.ruff check --select E,F,Iclean onfeatures.py,_engine_ast_utils.py,rules.py,test_rules.py,app.py.cd osprey_ui && npx tsc --noEmitclean.useEffect(except an explanatory comment), nouseState, no unbraced arrow callbacks, no deprecatedlabelStyle.Checklist
uv run ruff check .passes —--select E,F,Iclean on all touched filesuv tool run fawltydeps --check-unused --pyenv .venvpasses — pendingCHANGELOG.mdwith my changes, if applicable — not applicable (no CHANGELOG convention spotted in repo)🤖 Generated with Claude Code