Repository navigation
feat(flows): consolidated save+enable pre-authorization card (Approve all / Deny) - #5248
Conversation
… all / Deny)
When a flow is saved and enabled, show ONE card listing every ApprovalGate
permission a run will need, with exactly two actions — Approve all / Deny —
instead of parking the first run node-by-node on per-tool approval prompts.
Core (approval domain, always-on):
- flow_tool_trust gains list_flow_trust / delete_flow_trust store fns +
gate wrappers; flows_delete now purges a deleted flow's grants.
- New RPC approval.preauthorize_flow { flow_id, tool_names } batch-grants
flow-scoped trust (idempotent; INSERT OR IGNORE) and writes one
born-decided approve_always_for_flow audit row per new grant so blanket
approvals stay inspectable in Settings -> Approval history. A missing
gate (OPENHUMAN_APPROVAL_GATE=0) reports gate_installed=false and
succeeds — nothing ever parks in that mode.
Core (flows domain, gated):
- compute_approval_manifest(graph) statically mirrors the runtime gating
(caps.rs classifiers reused, never re-implemented): http_request ->
flows_http_request, code -> flows_code, native oh: tools via
classify_tool_call, Composio slugs via classify_composio_action_for_tier.
Curated Read actions are excluded (never gated); tier-Blocked classes are
"blocked" (informational); `=`-expression slugs are "dynamic"; agent_ref
agent nodes are disclosed ("may still ask").
- New RPC flows.approval_manifest { id | graph } joins the manifest against
existing trust -> { entries, missing, already_trusted, gate_installed }.
- dry_run_workflow output gains a quiet permissions_manifest key (both ok
and failure shapes) so the builder can mention upcoming permissions
without ever prompting during authoring dry runs.
Frontend:
- useFlowPreauthorization hook orchestrates the three enable moments:
Flows list toggle, canvas Save (incl. deferred draft navigation so the
card survives the /flows/:id route change), and the chat proposal card's
"Save & enable". Approve all = preauthorize then enable; Deny = flow
stays saved but disabled. Fail-open: a broken manifest RPC falls back to
plain enable (runtime gate still parks per-node as before).
- FlowPreauthorizationCard renders approvable/blocked/dynamic/agent rows
with exactly two actions; overlay variant for page contexts.
- i18n: flows.enableApproval.* added to en + all 13 locales.
Tests: approval store round-trips (list/delete/preauthorization audit),
manifest classification incl. curated-read exclusion + readonly-tier
blocking + dedupe + dynamic/agent disclosure, flows/approval schema
registries, hook behavior (grant-then-enable ordering, deny leaves
disabled, fail-open), and card rendering. Disabled-feature build
(--no-default-features --features tokenjuice-treesitter) checked clean.
|
Caution Review failedThe pull request is closed. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (21)
📝 WalkthroughWalkthroughAdds workflow approval-manifest computation, flow-scoped pre-authorization APIs and persistence, a reusable approval card, and integration across workflow saving, enabling, canvas navigation, and proposal handling. ChangesFlow approval manifest and backend APIs
Approval UI and workflow integration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant FlowCanvasPage
participant useFlowPreauthorization
participant ApprovalManifestRPC
participant PreauthorizeRPC
User->>FlowCanvasPage: Save or enable workflow
FlowCanvasPage->>useFlowPreauthorization: beginEnable or checkAfterSave
useFlowPreauthorization->>ApprovalManifestRPC: request manifest
ApprovalManifestRPC-->>useFlowPreauthorization: return missing grants
useFlowPreauthorization-->>FlowCanvasPage: show approval overlay
User->>FlowCanvasPage: Approve all or deny
FlowCanvasPage->>useFlowPreauthorization: approveAll or deny
useFlowPreauthorization->>PreauthorizeRPC: grant missing tools
PreauthorizeRPC-->>useFlowPreauthorization: return approval result
useFlowPreauthorization-->>FlowCanvasPage: settle outcome and continue navigation or enablement
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Cover the new API clients (getApprovalManifest / preauthorizeFlow), the hook's checkAfterSave and deny failure paths, the page overlay wrapper, and the WorkflowProposalCard inline card branches (card shown on missing grants, Approve all -> terminal saved view without a redundant enable, Deny -> saved-but-disabled note). Diff coverage was 68%, gate needs 80%.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/components/chat/WorkflowProposalCard.tsx (1)
244-260: 🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy liftDuplicate flow creation: Save stays clickable while the pre-auth card is pending.
When
createFlowreturns an already-enabled flow andcheckAfterSaveshows the card,save()returns without ever callingsetSavedFlowId(flow.id), andsetSaving(false)(Line 253) re-enables the Save button while the decision is still pending. A repeat click on Save re-enterssave()withflowId = savedFlowId = nulland callscreateFlowagain, persisting a second flow server-side.Fix both the missing state and the UI guard:
🛡️ Proposed fix
if (flow.enabled) { // Already live — surface the pre-authorization card when grants // are missing; otherwise this is the terminal success state. log('save: createFlow returned enabled id=%s — running pre-auth check', flow.id); + // Persist the id now: the Save button stays clickable while the + // card is pending below, and without this a retry click would + // call `createFlow` again and create a duplicate flow. + setSavedFlowId(flow.id); const cardShown = await preauth.checkAfterSave(flow.id, true);Also gate the Save button itself so a repeat click can't re-enter
save()at all while a decision is pending:<Button variant="primary" size="sm" data-analytics-id="workflow-proposal-save" onClick={() => void save()} disabled={saving || !!preauth.pending}>A regression test for "click Save again while the preauth card is showing →
createFlowis called only once" would be worth adding alongside this fix.Also applies to: 376-383
🤖 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 `@app/src/components/chat/WorkflowProposalCard.tsx` around lines 244 - 260, Prevent duplicate flow creation while pre-authorization is pending: in save(), persist the created flow ID with setSavedFlowId(flow.id) before handling the enabled-flow preauth path, and keep the saving state disabled while preauth.checkAfterSave displays a decision card. Update the Save Button’s disabled condition to include preauth.pending alongside saving, ensuring repeat clicks cannot re-enter save() during the pending decision.
🧹 Nitpick comments (3)
src/openhuman/flows/ops.rs (1)
7191-7264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd RPC-level tests for
flows_approval_manifest
src/openhuman/flows/ops_tests.rscoverscompute_approval_manifest, but not the wrapper’sflow_tool_trustjoin or thegate_installed = falseshort-circuit. Add cases for a saved flow with partial trust (missing+already_trusted) and for the no-gate path.🤖 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 `@src/openhuman/flows/ops.rs` around lines 7191 - 7264, Add RPC-level tests for flows_approval_manifest covering a saved flow with partial flow_tool_trust, asserting trusted tools appear in already_trusted and untrusted tools in missing, plus a no-gate case asserting gate_installed is false and missing is empty. Reuse the existing test setup and fixtures in ops_tests.rs, exercising the wrapper rather than only compute_approval_manifest.Source: Coding guidelines
app/src/components/flows/FlowPreauthorizationCard.tsx (1)
119-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winConsider modal semantics for the overlay's alertdialog.
FlowPreauthorizationOverlayrenders a full-screen modal but the innerrole="alertdialog"has noaria-modal="true"and nothing moves focus into it on mount, so screen-reader users may not get correct modal semantics or keyboard focus. NoteFlowPreauthorizationCardis also reused inline (non-modal) in chat per its own doc comment, soaria-modalshouldn't be hardcoded on the card itself — it belongs on/near the overlay wrapper (or behind amodalprop passed to the card).🤖 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 `@app/src/components/flows/FlowPreauthorizationCard.tsx` around lines 119 - 133, The FlowPreauthorizationOverlay needs complete modal accessibility semantics while FlowPreauthorizationCard remains reusable inline. Update the overlay/card integration to mark only the overlay instance as modal with aria-modal="true" and move keyboard focus into the alertdialog when it mounts, using an appropriate focusable target or ref; do not hardcode aria-modal on FlowPreauthorizationCard itself.app/src/components/chat/WorkflowProposalCard.tsx (1)
409-409: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winHardcoded translation key ignores
preauth.errorKey's actual value.
errorMsg={preauth.errorKey ? t('flows.enableApproval.error') : null}discards whatever key the hook returns and always renders the same literal key. Works today only because the hook currently ever sets one error key.♻️ Proposed fix
- errorMsg={preauth.errorKey ? t('flows.enableApproval.error') : null} + errorMsg={preauth.errorKey ? t(preauth.errorKey) : null}🤖 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 `@app/src/components/chat/WorkflowProposalCard.tsx` at line 409, Update the errorMsg expression in WorkflowProposalCard to translate the actual preauth.errorKey value when present, rather than always using the hardcoded flows.enableApproval.error key; retain null when no error key is returned.
🤖 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 `@app/src/components/chat/WorkflowProposalCard.tsx`:
- Around line 216-230: Add grep-friendly diagnostics in WorkflowProposalCard
around the approved onSettled transition and the preauth.beginEnable external
call: log entry/arguments and completion or error outcomes for beginEnable, and
log the approved path before markSourceMessageConsumed/markCompleted. Preserve
the existing no-card and denied behavior and avoid changing flow semantics.
In `@app/src/components/flows/FlowPreauthorizationCard.tsx`:
- Around line 66-91: Update the row key in the entries.map callback within
FlowPreauthorizationCard to use entry.node_id alone, since it is always present
and uniquely identifies each node. Remove the tool_name/kind-based key
construction while preserving the existing rendering and test identifiers.
In `@app/src/lib/i18n/de.ts`:
- Line 4462: Update the translation value for the
“flows.enableApproval.approveAll” key to use “genehmigen,” matching the
terminology used by the related approval strings while preserving the existing
meaning.
In `@app/src/lib/i18n/fr.ts`:
- Around line 4447-4450: Update the French translations for
flows.enableApproval.dynamicHint and flows.enableApproval.agentHint so both
explicitly state that approval may be requested, replacing the ambiguous “il
vous demandera” phrasing and completing the grammatical construction around
“peut demander séparément.”
In `@app/src/lib/i18n/hi.ts`:
- Line 4337: Update the Hindi translation for the
`flows.enableApproval.approveAll` key from `सभी स्वीकृत करें` to the more
natural `सभी को अनुमति दें`, preserving the existing key and translation
structure.
In `@app/src/pages/FlowCanvasPage.tsx`:
- Around line 613-628: The new deferred-navigation flow lacks grep-friendly
diagnostics. Add verbose logs in the preauth onSettled callback around the
parked-navigation match and navigation transition, and in the
wasDraft/preauthCardShown branch around the deferred versus immediate navigation
decision; include the flow ID and selected path, while preserving the existing
navigation behavior.
- Around line 640-655: Update onCanvasSave to catch failures from
preauth.checkAfterSave in both branches. In the result.wasDraft path, ensure
navigation to /flows/${result.flowId} still occurs when the preauthorization
check throws, while preserving the existing cardShown behavior; in the non-draft
path, attach the established logging catch pattern to the fire-and-forget
promise to prevent unhandled rejections.
In `@app/src/pages/FlowsPage.tsx`:
- Around line 141-161: Add a verbose, grep-friendly diagnostic in handleToggle
immediately after preauth.beginEnable returns, recording whether enabling
occurred immediately or was deferred to the pre-authorization card; preserve the
existing listFlows refresh only for the immediate-enable outcome.
In `@src/openhuman/flows/ops.rs`:
- Around line 7117-7147: In the native-tool handling branch, update the result
of classify_tool_call before push_gated to skip entries whose class is
CommandClass::Read. Add a continue for read-only tools, matching the existing
Composio branch behavior, while preserving gating for all other command classes.
In `@src/openhuman/flows/schemas.rs`:
- Around line 1046-1076: Update the "approval_manifest" ControllerSchema output
definition to declare the four flat response fields returned by
ops::flows_approval_manifest: entries, missing, already_trusted, and
gate_installed. Remove the manifest wrapper and align the field types, required
flags, and descriptions with the existing flat "validate" schema pattern.
---
Outside diff comments:
In `@app/src/components/chat/WorkflowProposalCard.tsx`:
- Around line 244-260: Prevent duplicate flow creation while pre-authorization
is pending: in save(), persist the created flow ID with setSavedFlowId(flow.id)
before handling the enabled-flow preauth path, and keep the saving state
disabled while preauth.checkAfterSave displays a decision card. Update the Save
Button’s disabled condition to include preauth.pending alongside saving,
ensuring repeat clicks cannot re-enter save() during the pending decision.
---
Nitpick comments:
In `@app/src/components/chat/WorkflowProposalCard.tsx`:
- Line 409: Update the errorMsg expression in WorkflowProposalCard to translate
the actual preauth.errorKey value when present, rather than always using the
hardcoded flows.enableApproval.error key; retain null when no error key is
returned.
In `@app/src/components/flows/FlowPreauthorizationCard.tsx`:
- Around line 119-133: The FlowPreauthorizationOverlay needs complete modal
accessibility semantics while FlowPreauthorizationCard remains reusable inline.
Update the overlay/card integration to mark only the overlay instance as modal
with aria-modal="true" and move keyboard focus into the alertdialog when it
mounts, using an appropriate focusable target or ref; do not hardcode aria-modal
on FlowPreauthorizationCard itself.
In `@src/openhuman/flows/ops.rs`:
- Around line 7191-7264: Add RPC-level tests for flows_approval_manifest
covering a saved flow with partial flow_tool_trust, asserting trusted tools
appear in already_trusted and untrusted tools in missing, plus a no-gate case
asserting gate_installed is false and missing is empty. Reuse the existing test
setup and fixtures in ops_tests.rs, exercising the wrapper rather than only
compute_approval_manifest.
🪄 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
Run ID: 9fdee637-cfc1-4b79-9953-dc48e2aa1c98
📒 Files selected for processing (37)
app/src/components/chat/WorkflowProposalCard.test.tsxapp/src/components/chat/WorkflowProposalCard.tsxapp/src/components/flows/FlowPreauthorizationCard.test.tsxapp/src/components/flows/FlowPreauthorizationCard.tsxapp/src/hooks/useFlowPreauthorization.test.tsapp/src/hooks/useFlowPreauthorization.tsapp/src/lib/i18n/ar.tsapp/src/lib/i18n/bn.tsapp/src/lib/i18n/de.tsapp/src/lib/i18n/en.tsapp/src/lib/i18n/es.tsapp/src/lib/i18n/fr.tsapp/src/lib/i18n/hi.tsapp/src/lib/i18n/id.tsapp/src/lib/i18n/it.tsapp/src/lib/i18n/ko.tsapp/src/lib/i18n/pl.tsapp/src/lib/i18n/pt.tsapp/src/lib/i18n/ru.tsapp/src/lib/i18n/zh-CN.tsapp/src/pages/FlowCanvasPage.tsxapp/src/pages/FlowsPage.tsxapp/src/services/api/approvalApi.test.tsapp/src/services/api/approvalApi.tsapp/src/services/api/flowsApi.test.tsapp/src/services/api/flowsApi.tsdocs/TEST-COVERAGE-MATRIX.mdsrc/openhuman/approval/gate.rssrc/openhuman/approval/rpc.rssrc/openhuman/approval/schemas.rssrc/openhuman/approval/store.rssrc/openhuman/approval/types.rssrc/openhuman/flows/builder_tools.rssrc/openhuman/flows/ops.rssrc/openhuman/flows/ops_tests.rssrc/openhuman/flows/schemas.rssrc/openhuman/tinyflows/caps.rs
- Guard the draft-save preauth check so a failed manifest fetch can never strand the route on /flows/draft (a Save retry would createFlow again and duplicate the flow); catch the fire-and-forget check on the non-draft path. - Declare flows.approval_manifest's real flat output shape (entries/missing/ already_trusted/gate_installed) instead of a phantom 'manifest' wrapper. - Key manifest rows by node_id (tool_name is a trust key, not a row id). - Add grep-friendly logs on the approved settle, beginEnable boundaries, and the deferred-vs-immediate navigation branches. - Locale polish: de 'Alle genehmigen', fr approval-hint wording, hi 'सभी को अनुमति दें'.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/lib/i18n/zh-CN.ts (1)
4108-4108: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse the established “召回” terminology.
memory.recallis already translated as “召回” elsewhere in this locale. Using “回忆” here makes the memory retrieval operation sound like personal recollection rather than a technical recall action.🤖 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 `@app/src/lib/i18n/zh-CN.ts` at line 4108, Update the translation value for flows.nodeConfig.memory.operation_recall in the zh-CN locale to use the established “召回” terminology, matching the existing memory.recall translation elsewhere in the locale.
🤖 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.
Outside diff comments:
In `@app/src/lib/i18n/zh-CN.ts`:
- Line 4108: Update the translation value for
flows.nodeConfig.memory.operation_recall in the zh-CN locale to use the
established “召回” terminology, matching the existing memory.recall translation
elsewhere in the locale.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 35e3e8ff-9262-44ae-84b1-662fde510b02
📒 Files selected for processing (22)
app/src/components/chat/WorkflowProposalCard.tsxapp/src/components/flows/FlowPreauthorizationCard.tsxapp/src/lib/i18n/ar.tsapp/src/lib/i18n/bn.tsapp/src/lib/i18n/de.tsapp/src/lib/i18n/en.tsapp/src/lib/i18n/es.tsapp/src/lib/i18n/fr.tsapp/src/lib/i18n/hi.tsapp/src/lib/i18n/id.tsapp/src/lib/i18n/it.tsapp/src/lib/i18n/ko.tsapp/src/lib/i18n/pl.tsapp/src/lib/i18n/pt.tsapp/src/lib/i18n/ru.tsapp/src/lib/i18n/zh-CN.tsapp/src/pages/FlowCanvasPage.tsxapp/src/pages/FlowsPage.tsxdocs/TEST-COVERAGE-MATRIX.mdsrc/openhuman/flows/builder_tools.rssrc/openhuman/flows/schemas.rssrc/openhuman/tinyflows/caps.rs
🚧 Files skipped from review as they are similar to previous changes (21)
- app/src/lib/i18n/de.ts
- app/src/lib/i18n/ru.ts
- app/src/lib/i18n/en.ts
- app/src/lib/i18n/it.ts
- app/src/lib/i18n/bn.ts
- app/src/lib/i18n/id.ts
- app/src/lib/i18n/pt.ts
- app/src/lib/i18n/ko.ts
- app/src/lib/i18n/hi.ts
- app/src/components/flows/FlowPreauthorizationCard.tsx
- app/src/lib/i18n/fr.ts
- app/src/lib/i18n/es.ts
- app/src/pages/FlowsPage.tsx
- app/src/lib/i18n/ar.ts
- app/src/lib/i18n/pl.ts
- docs/TEST-COVERAGE-MATRIX.md
- src/openhuman/flows/builder_tools.rs
- app/src/components/chat/WorkflowProposalCard.tsx
- src/openhuman/tinyflows/caps.rs
- src/openhuman/flows/schemas.rs
- app/src/pages/FlowCanvasPage.tsx
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b23a5ee4a4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const cardShown = await preauth.checkAfterSave(flow.id, true); | ||
| setSaving(false); |
There was a problem hiding this comment.
Record the saved flow before showing preauth
When createFlow returns an already-enabled flow and checkAfterSave opens the pre-authorization card, this branch sets saving back to false and returns without storing flow.id in savedFlowId. The proposal controls are only disabled while saving, so clicking Save again while the card is pending starts from savedFlowId === null and calls createFlow a second time, creating duplicate saved flows instead of retrying the enable/preauth path for the first one.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This was fixed in 3ee5498 (also flagged by greptile): setSavedFlowId(flow.id) now runs immediately after createFlow persists, before any branch releases saving, so a second Save click while the card is pending reuses the persisted id — a regression test pins createFlow at one call across a double click.
| if (!manifest || !manifest.gate_installed || manifest.missing.length === 0) { | ||
| await setFlowEnabled(flowId, true); |
There was a problem hiding this comment.
Do not skip blocked-only manifests
For a manifest that contains only blocked rows, such as a readonly user enabling a workflow with only HTTP/code actions, missing is empty because blocked entries are intentionally not approvable; this branch therefore enables the flow without showing the informational card. The runtime tier gate still returns Block, so the user gets no save-time warning and only discovers the refusal when the run fails; the repo’s autonomy model treats Block as a distinct gate decision that should not be collapsed into the no-card success path.
AGENTS.md reference: AGENTS.md:L101-L108
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in df3dd92 — the card now surfaces whenever the manifest contains blocked rows, not only when grants are missing. For a blocked-only manifest the primary action reads 'Enable anyway' (nothing to grant, so the preauthorize RPC is skipped) and the ⛔ rows carry the 'blocked by your agent access settings' hint, so the user sees the tier Block at save time instead of a failing run. Deny still leaves the flow disabled. Covered by new hook + card tests.
|
| Filename | Overview |
|---|---|
| app/src/hooks/useFlowPreauthorization.ts | Core orchestration hook: two entry points (beginEnable / checkAfterSave), fail-open on manifest errors, clean approve/deny state machine. The checkAfterSave path called fire-and-forget in the non-draft canvas save arm means a Deny outcome never syncs local flow.enabled state (existing unresolved P1 comment). |
| app/src/pages/FlowCanvasPage.tsx | Integrates useFlowPreauthorization for both draft-create (deferred navigation via preauthNavRef) and copilot-proposal accept paths. Non-draft checkAfterSave remains fire-and-forget; a Deny in that path disables the flow on the backend without updating FlowEditor's local enabled state (unresolved P1 from prior review). |
| app/src/pages/FlowsPage.tsx | handleToggle correctly splits enable/disable paths; disable is direct, enable routes through beginEnable. Overlay blocks UI while pending. Post-settle list refresh via onSettled is correct. |
| app/src/components/chat/WorkflowProposalCard.tsx | setSavedFlowId now set before any branch releases saving (addressed the prior P1 duplicate-flow bug). preauth card rendered inline below the proposal body; onSettled routes approved/denied outcomes correctly. |
| app/src/components/flows/FlowPreauthorizationCard.tsx | Presentational component with correct role=alertdialog, per-kind icons and hints, blocked-only Enable anyway label swap, and disabled state during busy. |
| src/openhuman/flows/ops.rs | compute_approval_manifest reuses runtime classifiers for correctness at save time. flows_approval_manifest joins against existing flow_tool_trust grants. flows_delete_impl now purges flow trust grants as best-effort cleanup. |
| src/openhuman/approval/store.rs | Three new store functions: record_flow_preauthorization (born-decided audit row), list_flow_trust (sorted, flow-scoped), delete_flow_trust (all or named, idempotent). All backed by thorough unit tests. |
| src/openhuman/approval/rpc.rs | approval_preauthorize_flow validates flow_id, enforces MAX_PREAUTHORIZE_TOOLS=100, returns gate_installed:false when gate absent, writes idempotent INSERT OR IGNORE per tool plus one audit row per new grant. |
| src/api/config.rs | Extracts backend_env_test_lock() as a crate-wide process-global mutex to prevent cross-module test races on BACKEND_URL env vars. |
Reviews (7): Last reviewed commit: "fix(flows): surface the card for blocked..." | Re-trigger Greptile
The vendored sdk bump (tinyhumansai#5260) added futures + tokio to tinyhumans-sdk's dependency set without regenerating either Cargo.lock, so every --locked cargo invocation (Rust Quality lane's TLS-policy check, coverage lanes) fails with 'cannot update the lock file'. Also picks up the tinyflows reqwest rename normalization from the same merge. No version changes.
…ome; unify backend env test locks The two medulla::ops tests asserted MedullaNoBaseUrl from a cleared env, but base_url() falls back to the hosted backend default BY DESIGN (resolve.rs's an_unconfigured_install_falls_back_to_the_hosted_backend pins it), so NoBaseUrl is unreachable that way and the tests failed deterministically whenever the full suite ran them (our PR always runs the full suite via the Cargo.lock fallback; per-area lanes never did). They now assert the state a cleared env actually produces - the missing session token - against a temp workspace so a signed-in dev machine cannot flip the outcome. Also collapses FOUR module-local env locks (api::config, core::cli_tests, medulla::ops, medulla::resolve) into one crate-wide backend_env_test_lock(): std::env is process-global, so module-local locks cannot prevent cross-module set_var/remove_var races on BACKEND_URL/VITE_BACKEND_URL/app-env.
…bility routing The raw-coverage voice e2e expected vision state 'ondemand' for the fixture id 'vision-ready', which the pre-tinyhumansai#5253 MVP allowlist rejected (fallback model absent from mock tags -> ondemand). tinyhumansai#5253's is_vision_capable heuristic accepts the id (vision marker in the name), the mock's /api/tags advertises it, and a present Ondemand-mode vision model correctly reports 'ready'. Stale expectation, intentional behavior change — only surfaced by the full-suite fallback this PR triggers.
…or preauthorize_flow The raw-coverage e2e pins the approval namespace's controller list, which now includes preauthorize_flow; also exercises the new handler's param validation (missing flow_id / missing + non-array + mixed-type tool_names). Success-path semantics stay pinned by the approval::rpc / approval::store unit tests — asserting them here would be order-dependent on the process-global gate installed by a sibling test.
… flow on re-click When createFlow returned enabled and the pre-authorization card was shown, save() released the saving lock without recording savedFlowId; a second 'Save & enable' click then re-ran createFlow and duplicated the flow (greptile P1). The id is now recorded immediately after persist, before any branch can release the lock, so a re-click reuses the persisted flow. Regression test pins createFlow at one call across a double click.
A manifest with only tier-Blocked rows (e.g. readonly tier + http/code nodes) had missing=[] and enabled silently; the user discovered the Block only when the run failed. The card now also shows when blocked rows exist — informational rows plus a primary action that reads 'Enable anyway' when nothing is approvable (the grant RPC is skipped; the runtime tier gate keeps blocking). Deny still leaves the flow off. New flows.enableApproval.enableAnyway key in all 14 locales. (chatgpt-codex-connector P2)
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
dry_run_workflowoutput gains an informationalpermissions_manifestkey; the card only fires at the save+enable commitment moment.approval.preauthorize_flowRPC; every new grant is a born-decidedapprove_always_for_flowrow in the durable approval audit trail (Settings → Approval history).flows.approval_manifestjoins against existingflow_tool_trust).=slugs andagent_refsteps are disclosed as "may still ask".Problem
The first run of any new flow with side-effect nodes feels broken: the run parks mid-execution once per external-effect node, interrupts the user repeatedly, and silently dies (10-min TTL → cancelled) if nobody is watching. The flow-scoped trust primitive (
approve_always_for_flow→flow_tool_trust) already existed but was only reachable reactively, one parked card at a time.Solution
compute_approval_manifest(graph)statically enumerates the trust keys a run needs by reusing the exact runtime classifiers (classify_tool_call,classify_composio_action_for_tier— madepub(crate); never a parallel re-implementation, so the card can't drift from what actually gates). Newflows.approval_manifest { id | graph }RPC returns{ entries, missing, already_trusted, gate_installed }.flows_deletepurges the flow's grants.list_flow_trust/delete_flow_trust/record_flow_preauthorizationstore fns +approval.preauthorize_flowRPC (succeeds withgate_installed: falsewhen the gate is disabled — nothing parks in that mode, so the UX stays identical).useFlowPreauthorizationhook orchestrates the three enable moments (Flows list toggle, canvas Save with deferred draft navigation, chat proposal "Save & enable");FlowPreauthorizationCard+ overlay variant; fail-open on manifest errors (falls back to plain enable — the runtime gate still parks per-node exactly as before).requires_approval→ tinyflows interrupt →flows_resume) are deliberately untouched — pre-authorization clears only implicit tool prompts, never author-requested checkpoints.Submission Checklist
diff-cover) meet the gate enforced by.github/workflows/ci-lite.yml. Runpnpm test:coverageandpnpm test:rustlocally; PRs below 80% on changed lines will not merge.docs/TEST-COVERAGE-MATRIX.mdreflect this change (orN/A: behaviour-only change)## RelatedCloses #NNNin the## Relatedsection — N/A: no pre-existing issue; feature designed and implemented in-sessionImpact
flow_tool_trust), auditable (born-decided audit rows), and cannot clear tier-Blocked classes or the curation allowlist.OPENHUMAN_APPROVAL_GATE=0behaviour unchanged.--no-default-features --features tokenjuice-treesitterbuild checked clean (feature-gate drift catcher).Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check(prettier run on all touched files)pnpm typecheckvitest relatedover all touched frontend files (46 files, 614 tests passed);cargo test --libfor approval store/gate/schemas + flows ops/schemas suites (243 tests passed)cargo fmt+cargo check(default and--no-default-features --features tokenjuice-treesitter)app/src-taurichangesValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
OPENHUMAN_APPROVAL_GATE=0and readonly-tier blocking unchanged.Duplicate / Superseded PR Handling
Summary by CodeRabbit