Skip to content

Improve Action Review Across Heartwood Interfaces - #95

Merged
PSchmiedmayer merged 5 commits into
mainfrom
feature/action-review-experience
Jul 26, 2026
Merged

Improve Action Review Across Heartwood Interfaces#95
PSchmiedmayer merged 5 commits into
mainfrom
feature/action-review-experience

Conversation

@PSchmiedmayer

@PSchmiedmayer PSchmiedmayer commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

♻️ Current situation & Problem

Action review used different terminology and presentation across interfaces, obscured grouped-decision scope, and could leave a cached session service on stale project settings. This advances #26 and #41 while remaining independent of the runtime work in #24.

⚙️ Release Notes

  • Present complete grouped action sets with exact arguments and safe allow-or-reject controls in the terminal and browser.
  • Add shared Review Every Action and Low-Risk Automation settings across the CLI, browser, and notebook bridge.
  • Refresh session services after project configuration changes and serialize settings updates against active turns.

📚 Documentation

Updated the terminal, browser, action-review, security, and command guidance with current terminology and regenerated browser screenshots.

✅ Testing

  • 935 Python tests with 90.22% coverage
  • 81 web tests and 3 Playwright workflows
  • Gateway, Jupyter proxy, reference analysis, strict documentation, and versioned documentation smoke tests
  • Ruff, mypy, ESLint, Prettier, TypeScript, and production build checks

Code of Conduct & Contributing Guidelines

By creating and submitting this pull request, you agree to follow our Code of Conduct and Contributing Guidelines:

Copilot AI review requested due to automatic review settings July 26, 2026 00:26
@github-project-automation github-project-automation Bot moved this to Backlog in Heartwood Jul 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change standardizes action-review terminology and metadata across Heartwood. The gateway now exposes policy-aware mode and presentation data, validates persisted modes, synchronizes configuration access, and refreshes cached services. The terminal adds /permissions and modal mode selection. The web UI adds action-review settings, grouped action decisions, risk/tool labels, and locked states. Notebook, compliance output, smoke tests, and documentation are updated to match the revised terminology and behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Researcher
  participant WebUI
  participant SessionGateway
  participant ActionService
  Researcher->>WebUI: open action-review settings
  WebUI->>SessionGateway: select allowed mode
  SessionGateway->>ActionService: apply configuration
  ActionService-->>SessionGateway: return action-set state
  SessionGateway-->>WebUI: render grouped actions
  Researcher->>WebUI: allow or reject complete set
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: standardizing action review across Heartwood interfaces.
Description check ✅ Passed The description is directly related to the changeset and matches the documented goals and scope.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI review requested due to automatic review settings July 26, 2026 00:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 26, 2026 00:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/cli/src/heartwood/cli/__init__.py (1)

833-837: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the default-mode label instead of hardcoding it.

"Review Every Action" duplicates the label that ACTION_MODE_OPTIONS owns, while line 860 selects "always-confirm" through the gateway. Using action_mode_label("always-confirm") keeps setup output aligned if the label changes.

♻️ Proposed refactor
     print(
         "  Action review: Existing project setting"
         if resume_existing
-        else "  Action review: Review Every Action"
+        else f"  Action review: {action_mode_label('always-confirm')}"
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/heartwood/cli/__init__.py` around lines 833 - 837, Update
the action review output near the resume_existing conditional to derive the
default-mode text through action_mode_label("always-confirm") instead of
hardcoding "Review Every Action". Preserve the existing "Existing project
setting" branch and reuse the ACTION_MODE_OPTIONS-backed helper so the setup
label stays synchronized with the selected gateway mode.
packages/webui/src/components/SessionRail.tsx (1)

21-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

UtilityPanel is now declared identically in both SessionRail.tsx and UtilitySheet.tsx.

Adding a member requires editing both. Consider exporting it once (e.g. from ../types) and re-exporting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/webui/src/components/SessionRail.tsx` around lines 21 - 22,
Consolidate the duplicated UtilityPanel type used by SessionRail and
UtilitySheet into a single shared declaration, preferably in the existing types
module, and import or re-export it from both components. Preserve the current
union members and update references so adding future members requires changing
only the shared type.
packages/webui/src/components/ConversationWorkspace.tsx (1)

288-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Key the focus effect on the representative's id, not object identity.

setRepresentative is a fresh element reference whenever buildViewModel re-runs (new events arrive), so the heading can steal focus repeatedly while the same action set is pending — e.g. while the researcher is reading the expanded arguments.

♻️ Suggested change
-  useEffect(() => {
-    if (setRepresentative) headingRef.current?.focus();
-  }, [setRepresentative]);
+  const representativeId = setRepresentative?.targetId ?? null;
+  useEffect(() => {
+    if (representativeId !== null) headingRef.current?.focus();
+  }, [representativeId]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/webui/src/components/ConversationWorkspace.tsx` around lines 288 -
294, Update the focus effect for setRepresentative in ConversationWorkspace so
its dependency tracks the representative’s stable id rather than the object
reference. Preserve the existing focus behavior when the action set changes,
while preventing refocus when buildViewModel recreates the same representative
object.
packages/webui/src/styles.css (1)

700-702: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer :focus-visible for suppressing the heading outline.

The heading is tabIndex={-1} and focused programmatically, so hiding the ring is reasonable, but :focus-visible keeps an indicator if the element ever becomes keyboard-reachable.

♻️ Suggested change
-.approval-introduction h2:focus {
+.approval-introduction h2:focus:not(:focus-visible) {
   outline: none;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/webui/src/styles.css` around lines 700 - 702, Update the
.approval-introduction h2 focus styling to use :focus-visible instead of :focus
when suppressing the outline, preserving the current programmatic-focus behavior
while allowing an indicator for keyboard-reachable focus.
packages/gateway/src/heartwood/gateway/_project_config.py (1)

363-394: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Clarify locked()’s non-reentrant FileLock behavior

locked() creates a new FileLock on each call. If a nested call happens in the same thread, filelock will catch the self-deadlock and raise at runtime rather than hang indefinitely, but the current helper contract/docs should make this explicit. Use a single is_singleton lock or re-shield the helper with a clear non-reentrancy requirement/error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gateway/src/heartwood/gateway/_project_config.py` around lines 363 -
394, The locked() helper currently creates a new FileLock per invocation without
documenting or enforcing its non-reentrant behavior. Update locked() and its
callers’ contract to clearly require non-nested use, or configure a shared
singleton FileLock so nested acquisition is handled explicitly; ensure any
unsupported nested call raises a clear error rather than relying on an opaque
self-deadlock failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/heartwood/cli/_tui.py`:
- Around line 428-445: Guard the self.session.action_settings() call in
action_show_permissions with the existing ActionSettingsError handling pattern,
and return without pushing ActionModeScreen when loading settings fails.
Preserve the current locked_reason calculation and _action_mode_selected
callback behavior for successful reads.

In `@packages/compliance/src/heartwood/compliance/_reviewer_packet.py`:
- Around line 148-150: Update the action-policy sentence in the reviewer packet
text to explicitly state that the deployment-policy opt-in `confirm-risky`
automatically allows only action sets composed entirely of low-risk actions,
replacing the incomplete “continues only sets” wording while preserving the
surrounding policy details.

---

Nitpick comments:
In `@packages/cli/src/heartwood/cli/__init__.py`:
- Around line 833-837: Update the action review output near the resume_existing
conditional to derive the default-mode text through
action_mode_label("always-confirm") instead of hardcoding "Review Every Action".
Preserve the existing "Existing project setting" branch and reuse the
ACTION_MODE_OPTIONS-backed helper so the setup label stays synchronized with the
selected gateway mode.

In `@packages/gateway/src/heartwood/gateway/_project_config.py`:
- Around line 363-394: The locked() helper currently creates a new FileLock per
invocation without documenting or enforcing its non-reentrant behavior. Update
locked() and its callers’ contract to clearly require non-nested use, or
configure a shared singleton FileLock so nested acquisition is handled
explicitly; ensure any unsupported nested call raises a clear error rather than
relying on an opaque self-deadlock failure.

In `@packages/webui/src/components/ConversationWorkspace.tsx`:
- Around line 288-294: Update the focus effect for setRepresentative in
ConversationWorkspace so its dependency tracks the representative’s stable id
rather than the object reference. Preserve the existing focus behavior when the
action set changes, while preventing refocus when buildViewModel recreates the
same representative object.

In `@packages/webui/src/components/SessionRail.tsx`:
- Around line 21-22: Consolidate the duplicated UtilityPanel type used by
SessionRail and UtilitySheet into a single shared declaration, preferably in the
existing types module, and import or re-export it from both components. Preserve
the current union members and update references so adding future members
requires changing only the shared type.

In `@packages/webui/src/styles.css`:
- Around line 700-702: Update the .approval-introduction h2 focus styling to use
:focus-visible instead of :focus when suppressing the outline, preserving the
current programmatic-focus behavior while allowing an indicator for
keyboard-reachable focus.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a153eb3b-43c9-4abe-86d2-23fbaeda54f2

📥 Commits

Reviewing files that changed from the base of the PR and between ace596e and fc1d60e.

⛔ Files ignored due to path filters (3)
  • documentation/assets/screenshots/browser-action-review.png is excluded by !**/*.png
  • documentation/assets/screenshots/browser-action-settings.png is excluded by !**/*.png
  • documentation/assets/screenshots/browser-conversation.png is excluded by !**/*.png
📒 Files selected for processing (44)
  • documentation/architecture/index.md
  • documentation/architecture/sessions-audit.md
  • documentation/index.md
  • documentation/models/connections.md
  • documentation/operate/security.md
  • documentation/reference/cli.md
  • documentation/start/index.md
  • documentation/use/actions-audit.md
  • documentation/use/browser.md
  • documentation/use/terminal.md
  • packages/adapters/src/heartwood/adapters/platform/carina.py
  • packages/cli/README.md
  • packages/cli/src/heartwood/cli/__init__.py
  • packages/cli/src/heartwood/cli/_interactive.py
  • packages/cli/src/heartwood/cli/_tui.py
  • packages/cli/tests/test_cli.py
  • packages/cli/tests/test_interactive.py
  • packages/compliance/src/heartwood/compliance/_reviewer_packet.py
  • packages/compliance/tests/test_reviewer_packet.py
  • packages/gateway/src/heartwood/gateway/__init__.py
  • packages/gateway/src/heartwood/gateway/_action_presentation.py
  • packages/gateway/src/heartwood/gateway/_action_settings.py
  • packages/gateway/src/heartwood/gateway/_gateway.py
  • packages/gateway/src/heartwood/gateway/_project_config.py
  • packages/gateway/src/heartwood/gateway/_readiness.py
  • packages/gateway/tests/test_action_settings.py
  • packages/gateway/tests/test_gateway_contract.py
  • packages/gateway/tests/test_project_config.py
  • packages/notebook/src/heartwood/notebook/_widgets.py
  • packages/notebook/tests/test_notebook.py
  • packages/webui/scripts/smoke-reference-analysis.cjs
  • packages/webui/src/App.test.tsx
  • packages/webui/src/App.tsx
  • packages/webui/src/actionPresentation.ts
  • packages/webui/src/client.test.ts
  • packages/webui/src/components/ConversationWorkspace.tsx
  • packages/webui/src/components/SessionRail.tsx
  • packages/webui/src/components/UtilitySheet.tsx
  • packages/webui/src/components/WorkspaceHeader.tsx
  • packages/webui/src/e2e/app.spec.ts
  • packages/webui/src/styles.css
  • packages/webui/src/types.ts
  • packages/webui/src/viewModel.test.ts
  • packages/webui/src/viewModel.ts

Comment thread packages/cli/src/heartwood/cli/_tui.py
Comment thread packages/compliance/src/heartwood/compliance/_reviewer_packet.py Outdated
Copilot AI review requested due to automatic review settings July 26, 2026 00:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 26, 2026 00:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@PSchmiedmayer
PSchmiedmayer merged commit 6d4867a into main Jul 26, 2026
22 checks passed
@PSchmiedmayer
PSchmiedmayer deleted the feature/action-review-experience branch July 26, 2026 01:13
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Heartwood Jul 26, 2026
@PSchmiedmayer PSchmiedmayer added this to the 0.3.0 milestone Jul 26, 2026
@PSchmiedmayer PSchmiedmayer self-assigned this Jul 26, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants