Policy UI/UX update#37
Merged
Merged
Conversation
Signed-off-by: JaredforReal <w13431838023@gmail.com>
Signed-off-by: JaredforReal <w13431838023@gmail.com>
Signed-off-by: JaredforReal <w13431838023@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the policies UX in the WebUI (more discoverable form editing, inline help, and policy/group context) and extends the backend policy engine to return and record policy provenance (which policy/rule matched) for display in the UI.
Changes:
- Policy evaluation now returns a
PolicyResultwith provenance (policy file/source + rule name) and the dispatcher records this on the envelope metadata. - WebUI policy editing gets form-first UX, inline help tooltips, and shows which groups reference a policy.
- Config editor adds a policy dropdown for groups (with “create new policy” flow), and envelope UI surfaces
groupand matched policy/rule.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_orchestrator.py | Updates tests for PolicyEngine.evaluate() returning PolicyResult and asserts provenance fields. |
| loom/orchestrator/policy.py | Introduces PolicyResult, tracks rule file provenance, and returns richer evaluation results. |
| loom/orchestrator/dispatcher.py | Consumes PolicyResult, logs provenance, and writes matched policy/rule info into envelope metadata. |
| loom/webui/frontend/src/pages/policies/RuleCard.tsx | Adds help-tooltips and switches “Tools” editing to a schema-driven multiselect. |
| loom/webui/frontend/src/pages/policies/PolicyEditor.tsx | Defaults editor mode to “Form” and reorders tabs. |
| loom/webui/frontend/src/pages/policies/PoliciesPanel.tsx | Fetches groups to show which groups reference each policy; displays usage inline in the list. |
| loom/webui/frontend/src/pages/config/ConfigFormEditor.tsx | Adds policy dropdown (and create-new) for group policies; shows group → policy mapping in source cards. |
| loom/webui/frontend/src/lib/types.ts | Adds group to the frontend Envelope type. |
| loom/webui/frontend/src/components/EnvelopeDetail.tsx | Displays envelope group and matched policy/rule metadata. |
| loom/webui/frontend/src/components/EnvelopeCard.tsx | Displays a small group badge next to source_id. |
| docs/policy-guide.md | Adds a policy guide documenting rule matching, fields, groups, and examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+126
to
+130
| # Store policy provenance on the envelope | ||
| envelope.metadata["matched_rule"] = result.rule_name | ||
| envelope.metadata["matched_policy"] = result.policy_file | ||
| envelope.metadata["matched_policy_source"] = result.policy_source | ||
|
|
Comment on lines
+145
to
+152
| const { data: policies = [] } = useQuery({ | ||
| queryKey: ["policies"], | ||
| queryFn: listPolicies, | ||
| }) | ||
| const policyNames = useMemo( | ||
| () => policies.map((p) => p.name.replace(/\.ya?ml$/, "")), | ||
| [policies], | ||
| ) |
Comment on lines
+669
to
+673
| const newName = window.prompt("New policy name (without .yaml):") | ||
| if (!newName) return | ||
| const template = `prompt: \nmodel: sonnet\nauto_approve: false\n` | ||
| savePolicy(`${newName}.yaml`, template).catch(() => {}) | ||
| onChange(newName) |
Comment on lines
165
to
187
| @@ -154,7 +171,7 @@ def load_action_by_name(self, name: str, policy_dir: Path) -> PolicyAction | Non | |||
| data = yaml.safe_load(f) | |||
| if not data or not isinstance(data, dict): | |||
| return None | |||
| return PolicyAction( | |||
| action = PolicyAction( | |||
| priority=data.get("priority", 1), | |||
| agent=data.get("agent", ""), | |||
| prompt=data.get("prompt", ""), | |||
| @@ -168,12 +185,23 @@ def load_action_by_name(self, name: str, policy_dir: Path) -> PolicyAction | Non | |||
| skills=data.get("skills", []), | |||
| cwd=data.get("cwd", ""), | |||
| ) | |||
Comment on lines
+49
to
+58
| // Build reverse map: policy stem → group names | ||
| const policyUsage = useMemo(() => { | ||
| const map: Record<string, string[]> = {} | ||
| for (const g of groups) { | ||
| const policyName = (g as unknown as Record<string, unknown>).policy as string | undefined | ||
| if (policyName) { | ||
| const stem = policyName.replace(/\.ya?ml$/, "") | ||
| if (!map[stem]) map[stem] = [] | ||
| map[stem].push(g.name) | ||
| } |
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.
No description provided.