Skip to content

feat(tool_policy): diagnostics RPC and Developer Options panel#2715

Open
MrMrVlad wants to merge 5 commits into
tinyhumansai:mainfrom
MrMrVlad:feat/2136-tool-policy-diagnostics
Open

feat(tool_policy): diagnostics RPC and Developer Options panel#2715
MrMrVlad wants to merge 5 commits into
tinyhumansai:mainfrom
MrMrVlad:feat/2136-tool-policy-diagnostics

Conversation

@MrMrVlad
Copy link
Copy Markdown
Contributor

@MrMrVlad MrMrVlad commented May 26, 2026

Summary

  • Extend tool_registry.diagnostics with policy posture, MCP allowlist summary, MCP write-audit health, and recent policy denials (redacted).
  • Record recent tool-policy blocks in a small in-memory ring buffer when the agent harness denies a tool call.
  • Add Developer Options → Diagnostics settings panel that renders the diagnostics RPC for operators and reviewers.

Problem

When tools are hidden or policy blocks execution, operators today must reconstruct state from logs and config. Issue #2136 asks for a single diagnostics surface showing inventory, policy mode, MCP allowlists, audit health, and recent denials—without exposing secrets.

Solution

  • Core: ToolPolicyDiagnostics now composes registry counts, autonomy posture, per-server MCP allow/deny list sizes, best-effort mcp_writes row count (24h), and up to 25 recent denials from tool_registry::denials.
  • Agent loop: on policy block in turn.rs, append a truncated, redacted denial record.
  • UI: ToolPolicyDiagnosticsPanel calls tool_registry.diagnostics and sections the payload for quick review.

This is the first slice of #2136 (observability + UI). Conformance reporting (runtime contract hash, hidden raw-write tool checks, copyable support bundle) can follow in a separate PR.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — CI gate enforced by .github/workflows/coverage.yml; local diff-cover not run pre-submit
  • Coverage matrix updated — N/A: diagnostics observability only; no matrix row add/remove/rename
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no matrix feature IDs touched
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated — N/A: read-only settings diagnostics surface; no release-cut change
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Runtime: Desktop settings UI + core RPC; no migration.
  • Security: Diagnostics are redacted; denial reasons truncated; no secrets in RPC payload.

Related

Test plan

  • cargo test tool_registry --lib (diagnostics + denials ring buffer)
  • pnpm testToolPolicyDiagnosticsPanel.test.tsx
  • pnpm format:check, pnpm lint, pnpm compile, pnpm rust:check (pre-push hook)

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/2136-tool-policy-diagnostics
  • Commit SHA: 3c63b46c1b9c5e8a265b33376932a272d412355a

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck / pnpm compile
  • Focused tests: cargo test tool_registry --lib, Vitest panel test
  • Rust fmt/check (if changed): pre-push pnpm rust:check
  • Tauri fmt/check (if changed): included in pnpm rust:check

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: operators can inspect tool-policy state via RPC and settings UI; recent denials visible after blocks occur in-session.
  • User-visible effect: new Developer Options → Diagnostics entry.

Parity Contract

  • Legacy behavior preserved: existing tool_registry.list / get unchanged.
  • Guard/fallback/dispatch parity checks: diagnostics loads config with existing timeout helper.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none known
  • Canonical PR: this PR
  • Resolution: N/A

Summary by CodeRabbit

  • New Features

    • Added a Policy Diagnostics entry in developer settings and a diagnostics panel showing policy posture, tool inventory, MCP allowlist/write-audit summaries, and recent blocked tool calls.
    • System now records recent tool policy denials (timestamped, redacted/truncated for safety) for audit visibility.
  • Tests

    • Added tests validating panel rendering and diagnostics data display.
  • Documentation

    • Added developer-facing translation strings for the diagnostics UI.

Review Change Stack

Vladimir Yastreboff and others added 2 commits May 27, 2026 08:20
Expand tool_registry.diagnostics with policy posture, MCP allowlist summary,
MCP write audit health, and a best-effort recent-denials buffer. Add a minimal
Developer Options UI panel and unit coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@MrMrVlad MrMrVlad requested a review from a team May 26, 2026 22:23
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ff59526-1abc-4d47-a02d-f62d093a9564

📥 Commits

Reviewing files that changed from the base of the PR and between 4e7a457 and a739c7d.

📒 Files selected for processing (1)
  • src/openhuman/tool_registry/denials.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/tool_registry/denials.rs

📝 Walkthrough

Walkthrough

Adds a recent-denial registry and records denials on blocked tool calls; extends diagnostics types and ops to be config-driven (posture, MCP allowlists, write-audit health, recent denials); updates RPC handler/tests; adds a settings panel, i18n strings, menu entry, and a route to surface diagnostics.

Changes

Tool-Policy Diagnostics Feature

Layer / File(s) Summary
Denial recording infrastructure
src/openhuman/tool_registry/denials.rs, src/openhuman/tool_registry/mod.rs, src/openhuman/agent/harness/session/turn.rs
New in-memory capped denial registry with record/list, redaction/truncation helpers, module exposure, and integration where tool-policy blocks a tool call.
Diagnostic data shapes
src/openhuman/tool_registry/types.rs, src/openhuman/tool_registry/mod.rs
Adds ToolPolicyPosture, McpAllowlistDiagnostics, McpServerAllowlistSummary, McpWriteAuditHealth, RecentPolicyDenial and updates ToolPolicyDiagnostics to include posture, allowlists, write-audit health, and recent denials.
Diagnostics computation & RPC
src/openhuman/tool_registry/ops.rs, src/openhuman/tool_registry/schemas.rs
diagnostics(&Config) computes posture from Config, aggregates MCP allowlists, queries mcp_writes for write-audit health, and includes recent denials; RPC handler loads config with timeout and forwards it to diagnostics. Tests updated to validate new fields.
Diagnostics settings panel
app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx, app/src/components/settings/panels/__tests__/ToolPolicyDiagnosticsPanel.test.tsx, app/src/lib/i18n/en.ts
Adds a React panel that calls tool_registry.diagnostics RPC (with loading/error/ready states), renders posture, inventory, MCP servers (truncated), write-audit summary, recent denials (truncated), i18n strings, and tests that mock the RPC.
Menu and route wiring
app/src/pages/Settings.tsx, app/src/components/settings/panels/DeveloperOptionsPanel.tsx
Registers the panel route at tool-policy-diagnostics and adds a DeveloperOptions menu entry with i18n keys and inline SVG icon linking to that route.

Sequence Diagram(s):

sequenceDiagram
  participant Settings as Browser Settings Panel
  participant RPC as handle_diagnostics (server)
  participant Ops as tool_registry::ops::diagnostics
  participant Denials as denials::list
  participant DB as chunk_store (mcp_writes)
  Settings->>RPC: GET diagnostics
  RPC->>RPC: load config (timeout)
  RPC->>Ops: diagnostics(&config)
  Ops->>Denials: list(MAX_RECENT)
  Ops->>DB: SELECT COUNT(*) FROM mcp_writes (last 24h)
  Ops-->>RPC: ToolPolicyDiagnostics
  RPC-->>Settings: JSON diagnostics
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • tinyhumansai/openhuman#2336: Related diagnostics RPC and schema work that this PR further refines with config-driven fields and recent denials.
  • tinyhumansai/openhuman#2258: Changes to DeveloperOptionsPanel menu modeling that interact with the new tool-policy-diagnostics entry.
  • tinyhumansai/openhuman#2137: Earlier changes to the tool-policy deny decision path that this PR records into the denial registry.

Suggested reviewers

  • senamakel

"I hop and log each blocked try,
trimming secrets so no keys fly.
Posture, servers, audits in view —
a tiny report for the debugging crew.
Hooray, diagnostics in a neat supply!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a diagnostics RPC and Developer Options panel for tool policy, which aligns with the primary modifications across both backend and frontend code.
Linked Issues check ✅ Passed The PR successfully implements all core coding objectives from issue #2136: diagnostics RPC reporting policy/tool visibility, MCP allowlist/audit health tracking, recent denial recording with redaction, and UI panel rendering without secrets.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing tool-policy diagnostics: backend denial recording/RPC, frontend panel/routing/i18n, and tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 94.44% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. working A PR that is being worked on by the team. labels May 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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/settings/panels/__tests__/ToolPolicyDiagnosticsPanel.test.tsx`:
- Line 46: The failing assertion in ToolPolicyDiagnosticsPanel.test.tsx uses an
over-escaped regex for the literal parentheses; update the expect that calls
screen.getByText(...) so the regex escapes the parentheses only once (match the
literal "(24h)" rather than backslash characters), i.e. change the pattern used
in the getByText assertion to escape parentheses correctly for "Recent (24h):".

In `@app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx`:
- Around line 78-224: ToolPolicyDiagnosticsPanel renders many hard-coded user
strings (e.g., "Loading…", "Diagnostics unavailable", section headers like
"Policy posture", labels like "Autonomy:", empty states) that must be localized;
import and use useT() in the ToolPolicyDiagnosticsPanel component and replace
every literal JSX string (status messages, headings, dt/dd labels, empty-state
text, list placeholders like "<unnamed>") with t('...') calls using descriptive
keys (e.g., toolPolicy.loading, toolPolicy.unavailable,
toolPolicy.policyPosture.autonomy, toolPolicy.inventory.totalTools,
toolPolicy.mcpAllowlists.enabledLabel, toolPolicy.emptyUnnamed, etc.), then add
those same keys and English values to app/src/lib/i18n/en.ts in this PR so
translations load correctly.

In `@src/openhuman/agent/harness/session/turn.rs`:
- Around line 1331-1336: The call to
crate::openhuman::tool_registry::denials::record currently forwards the raw
reason into durable telemetry (using call.name, self.tool_policy.name(),
blocked_action, reason); replace this by redacting sensitive content before the
boundary: obtain a sanitized_reason via an existing redaction helper (or add one
e.g., redact_reason(reason) that strips PII/secrets or returns a redaction
token) and pass sanitized_reason to denials::record instead of reason, ensuring
only the pre-redacted string is persisted.

In `@src/openhuman/tool_registry/denials.rs`:
- Around line 20-21: The trimmed+truncated denial text in variable `reason` (set
via `truncate_reason(reason.trim())`) is persisted and later surfaced, so you
must redact sensitive data before storing or including it in diagnostics:
replace or wrap the stored value with a redaction step (e.g., call a
`redact_reason`/`sanitize_reason` function or inline redaction logic)
immediately after truncation and before any persistence or diagnostic creation;
apply the same change to the other occurrences referenced around lines 56–65 so
no raw/unredacted `reason` is ever written to records or logs.
- Around line 71-88: Tests are flaky because they share a global denial buffer;
make them deterministic by resetting the registry at test start: add a
clear/reset helper (e.g., reset_denials() or clear_denials()) in the denials
module and call it at the beginning of record_truncates_and_bounds and
record_ignores_empty_tool so each test starts with an empty global state; keep
using record, list and MAX_DENIALS as-is but ensure tests invoke the new reset
helper before exercising record/list so no cross-test races occur.
🪄 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: 91e32884-03d9-4135-9820-b7a9907aa647

📥 Commits

Reviewing files that changed from the base of the PR and between 5c035d8 and 3c63b46.

📒 Files selected for processing (10)
  • app/src/components/settings/panels/DeveloperOptionsPanel.tsx
  • app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx
  • app/src/components/settings/panels/__tests__/ToolPolicyDiagnosticsPanel.test.tsx
  • app/src/pages/Settings.tsx
  • src/openhuman/agent/harness/session/turn.rs
  • src/openhuman/tool_registry/denials.rs
  • src/openhuman/tool_registry/mod.rs
  • src/openhuman/tool_registry/ops.rs
  • src/openhuman/tool_registry/schemas.rs
  • src/openhuman/tool_registry/types.rs

Comment thread app/src/components/settings/panels/__tests__/ToolPolicyDiagnosticsPanel.test.tsx Outdated
Comment thread app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx Outdated
Comment on lines +1331 to +1336
crate::openhuman::tool_registry::denials::record(
call.name.as_str(),
self.tool_policy.name(),
blocked_action,
reason,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid persisting raw policy reasons into the denial registry.

This call forwards reason directly into a retained diagnostics buffer. Add redaction at this boundary (or pass a pre-redacted reason) so policy-block telemetry cannot retain sensitive text.

As per coding guidelines, “Never log secrets or full PII; always redact sensitive data in debug logs”.

🤖 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/agent/harness/session/turn.rs` around lines 1331 - 1336, The
call to crate::openhuman::tool_registry::denials::record currently forwards the
raw reason into durable telemetry (using call.name, self.tool_policy.name(),
blocked_action, reason); replace this by redacting sensitive content before the
boundary: obtain a sanitized_reason via an existing redaction helper (or add one
e.g., redact_reason(reason) that strips PII/secrets or returns a redaction
token) and pass sanitized_reason to denials::record instead of reason, ensuring
only the pre-redacted string is persisted.

Comment thread src/openhuman/tool_registry/denials.rs Outdated
Comment thread src/openhuman/tool_registry/denials.rs
Vladimir Yastreboff and others added 2 commits May 27, 2026 08:50
Localize diagnostics panel strings, redact sensitive denial reasons before
persistence, and fix the Vitest assertion for Recent (24h) copy.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@MrMrVlad
Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit + CI feedback in 4e7a457 / follow-up:

  • Frontend test: fixed over-escaped Recent (24h) regex (root cause of Vitest + coverage failures)
  • i18n: localized all ToolPolicyDiagnosticsPanel copy via useT() + en.ts keys
  • Redaction: sensitive markers (Bearer, sk-, ghp_, PEM) redacted in denials::record before persistence
  • Tests: clear global denial buffer at start of each denials unit test for parallel determinism
  • Cleanup: removed unused RecentPolicyDenial import

Re-running CI — prior Rust core failure (memory_ingestion_status_reflects_initialized_client_snapshot) passes locally and appears unrelated/flaky.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/lib/i18n/en.ts`:
- Around line 516-543: The new i18n keys under devOptions.toolPolicyDiagnostics
(e.g., devOptions.toolPolicyDiagnostics.posture.autonomy, .inventory.totalTools,
.mcpAllowlists.summary, etc.) were added to app/src/lib/i18n/en.ts but not
propagated to the chunked locale files; update the matching en chunk file
(en-*.ts) and every non-English locale chunk (ar, bn, de, es, fr, hi, id, it,
ko, pt, ru, zh-CN) by adding the exact same key names and using the English
strings as placeholders so the UI won’t show missing text for chunked locales —
search for the devOptions.toolPolicyDiagnostics prefix to locate where to insert
the new keys in each chunk file.
🪄 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: d65c2d23-973f-4aaa-a5a0-9e4568113038

📥 Commits

Reviewing files that changed from the base of the PR and between 3c63b46 and 4e7a457.

📒 Files selected for processing (6)
  • app/src/components/settings/panels/DeveloperOptionsPanel.tsx
  • app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx
  • app/src/components/settings/panels/__tests__/ToolPolicyDiagnosticsPanel.test.tsx
  • app/src/lib/i18n/en.ts
  • src/openhuman/tool_registry/denials.rs
  • src/openhuman/tool_registry/ops.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/components/settings/panels/tests/ToolPolicyDiagnosticsPanel.test.tsx
  • app/src/components/settings/panels/DeveloperOptionsPanel.tsx
  • app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx
  • src/openhuman/tool_registry/ops.rs

Comment thread app/src/lib/i18n/en.ts
Comment on lines +516 to +543
'devOptions.toolPolicyDiagnosticsDesc':
'Tool inventory, policy posture, MCP allowlists, and recent blocks',
'devOptions.toolPolicyDiagnostics.loading': 'Loading…',
'devOptions.toolPolicyDiagnostics.unavailable': 'Diagnostics unavailable',
'devOptions.toolPolicyDiagnostics.posture.title': 'Policy posture',
'devOptions.toolPolicyDiagnostics.posture.autonomy': 'Autonomy:',
'devOptions.toolPolicyDiagnostics.posture.workspaceOnly': 'Workspace only:',
'devOptions.toolPolicyDiagnostics.posture.maxActionsPerHour': 'Max actions/hr:',
'devOptions.toolPolicyDiagnostics.posture.approvalMediumRisk': 'Approval (medium risk):',
'devOptions.toolPolicyDiagnostics.posture.blockHighRisk': 'Block high risk:',
'devOptions.toolPolicyDiagnostics.inventory.title': 'Inventory',
'devOptions.toolPolicyDiagnostics.inventory.totalTools': 'Total tools',
'devOptions.toolPolicyDiagnostics.inventory.enabledTools': 'Enabled tools',
'devOptions.toolPolicyDiagnostics.inventory.mcpStdioTools': 'MCP stdio tools',
'devOptions.toolPolicyDiagnostics.inventory.jsonRpcTools': 'JSON-RPC tools',
'devOptions.toolPolicyDiagnostics.mcpAllowlists.title': 'MCP allowlists',
'devOptions.toolPolicyDiagnostics.mcpAllowlists.summary':
'Enabled: {enabled} · Servers: {enabledCount}/{totalCount}',
'devOptions.toolPolicyDiagnostics.mcpAllowlists.unnamed': '<unnamed>',
'devOptions.toolPolicyDiagnostics.mcpAllowlists.allowDeny': 'allow={allowCount} deny={denyCount}',
'devOptions.toolPolicyDiagnostics.mcpWriteAudit.title': 'MCP write audit',
'devOptions.toolPolicyDiagnostics.mcpWriteAudit.summary':
'Enabled: {enabled} · Recent (24h): {recentRows}',
'devOptions.toolPolicyDiagnostics.recentBlocked.title': 'Recent blocked calls',
'devOptions.toolPolicyDiagnostics.recentBlocked.empty': 'No blocked calls recorded.',
'devOptions.toolPolicyDiagnostics.redactedSurfaces.title': 'Redacted surfaces',
'devOptions.toolPolicyDiagnostics.redactedSurfaces.summary':
'Write-capable: {writeCount} · Policy surfaces: {policyCount}',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking i18n parity for devOptions.toolPolicyDiagnostics.* keys"

# 1) Collect new key set from en.ts
EN_FILE="app/src/lib/i18n/en.ts"
if [[ ! -f "$EN_FILE" ]]; then
  echo "Missing $EN_FILE"
  exit 1
fi

keys=$(rg -n "'devOptions\.toolPolicyDiagnostics[^']*'" "$EN_FILE" \
  | sed -E "s/.*'([^']+)'.*/\1/" \
  | sort -u)

if [[ -z "${keys}" ]]; then
  echo "No toolPolicyDiagnostics keys found in en.ts"
  exit 0
fi

echo "Found keys:"
echo "$keys"
echo

# 2) Find locale/chunk files
mapfile -t locale_files < <(fd -p 'app/src/lib/i18n' -t f '^(en|ar|bn|de|es|fr|hi|id|it|ko|pt|ru|zh-CN)(-[0-9]+)?\.ts$' | sort)

if [[ ${`#locale_files`[@]} -eq 0 ]]; then
  echo "No locale files found under app/src/lib/i18n"
  exit 1
fi

missing_any=0
for f in "${locale_files[@]}"; do
  missing_keys=()
  while IFS= read -r k; do
    if ! rg -n "'$k'" "$f" >/dev/null; then
      missing_keys+=("$k")
    fi
  done <<< "$keys"

  if [[ ${`#missing_keys`[@]} -gt 0 ]]; then
    missing_any=1
    echo "MISSING in $f:"
    printf '  - %s\n' "${missing_keys[@]}"
    echo
  fi
done

if [[ $missing_any -eq 0 ]]; then
  echo "✅ All locale/chunk files contain the new keys."
else
  echo "❌ Some locale/chunk files are missing keys."
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 766


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking i18n parity for devOptions.toolPolicyDiagnostics.* keys"

EN_FILE="app/src/lib/i18n/en.ts"
if [[ ! -f "$EN_FILE" ]]; then
  echo "Missing $EN_FILE"
  exit 1
fi

# Collect all devOptions.toolPolicyDiagnostics.* keys from en.ts
keys_file="$(mktemp)"
rg -n "'devOptions\.toolPolicyDiagnostics[^']*'" "$EN_FILE" \
  | sed -E "s/.*'([^']+)'.*/\1/" \
  | sort -u > "$keys_file"

if [[ ! -s "$keys_file" ]]; then
  echo "No toolPolicyDiagnostics keys found in en.ts"
  exit 0
fi

echo "Key count: $(wc -l < "$keys_file")"

# Find locale/chunk files without process substitution
# (process substitution previously failed with /dev/fd issues in this environment)
locale_list_file="$(mktemp)"
fd -p 'app/src/lib/i18n' -t f '^(en|ar|bn|de|es|fr|hi|id|it|ko|pt|ru|zh-CN)(-[0-9]+)?\.ts$' \
  | sort > "$locale_list_file"

mapfile -t locale_files < "$locale_list_file"
if [[ ${`#locale_files`[@]} -eq 0 ]]; then
  echo "No locale files found under app/src/lib/i18n"
  exit 1
fi

missing_any=0
missing_report_file="$(mktemp)"

while IFS= read -r f; do
  missing_keys_file="$(mktemp)"
  while IFS= read -r k; do
    if ! rg -n "'$k'" "$f" >/dev/null; then
      echo "$k" >> "$missing_keys_file"
    fi
  done < "$keys_file"

  if [[ -s "$missing_keys_file" ]]; then
    missing_any=1
    {
      echo "MISSING in $f:"
      sed 's/^/  - /' "$missing_keys_file"
      echo
    } >> "$missing_report_file"
  fi

  rm -f "$missing_keys_file"
done < "$locale_list_file"

if [[ $missing_any -eq 0 ]]; then
  echo "✅ All locale/chunk files contain the new keys."
else
  echo "❌ Some locale/chunk files are missing keys:"
  cat "$missing_report_file"
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 310


🏁 Script executed:

#!/bin/bash
set -euo pipefail

EN_FILE="app/src/lib/i18n/en.ts"
if [[ ! -f "$EN_FILE" ]]; then
  echo "Missing $EN_FILE" >&2
  exit 1
fi

# Collect new keys from en.ts
keys_file="$(mktemp)"
rg -n "'devOptions\.toolPolicyDiagnostics[^']*'" "$EN_FILE" \
  | sed -E "s/.*'([^']+)'.*/\1/" \
  | sort -u > "$keys_file"

echo "Key count: $(wc -l < "$keys_file")"

# Enumerate locale/chunk files using find (no fd path flag ambiguity)
# Targets:
# - en.ts
# - en-<N>.ts
# - ar.ts, ar-<N>.ts, bn.ts, bn-<N>.ts, ..., zh-CN.ts, zh-CN-<N>.ts
locale_files_file="$(mktemp)"
find app/src/lib/i18n -type f \( \
  -name 'en.ts' -o -name 'en-*.ts' \
  -o -name 'ar.ts' -o -name 'ar-*.ts' \
  -o -name 'bn.ts' -o -name 'bn-*.ts' \
  -o -name 'de.ts' -o -name 'de-*.ts' \
  -o -name 'es.ts' -o -name 'es-*.ts' \
  -o -name 'fr.ts' -o -name 'fr-*.ts' \
  -o -name 'hi.ts' -o -name 'hi-*.ts' \
  -o -name 'id.ts' -o -name 'id-*.ts' \
  -o -name 'it.ts' -o -name 'it-*.ts' \
  -o -name 'ko.ts' -o -name 'ko-*.ts' \
  -o -name 'pt.ts' -o -name 'pt-*.ts' \
  -o -name 'ru.ts' -o -name 'ru-*.ts' \
  -o -name 'zh-CN.ts' -o -name 'zh-CN-*.ts' \
\) | sort > "$locale_files_file"

mapfile -t locale_files < "$locale_files_file"
echo "Locale files found: ${`#locale_files`[@]}"

missing_any=0
missing_report_file="$(mktemp)"

for f in "${locale_files[@]}"; do
  missing_keys_file="$(mktemp)"
  while IFS= read -r k; do
    if ! rg -n "'$k'" "$f" >/dev/null; then
      echo "$k" >> "$missing_keys_file"
    fi
  done < "$keys_file"

  if [[ -s "$missing_keys_file" ]]; then
    missing_any=1
    {
      echo "MISSING in $f:"
      sed 's/^/  - /' "$missing_keys_file"
      echo
    } >> "$missing_report_file"
  fi
  rm -f "$missing_keys_file"
done

if [[ $missing_any -eq 0 ]]; then
  echo "✅ All locale/chunk files contain the new keys."
else
  echo "❌ Some locale/chunk files are missing keys:"
  cat "$missing_report_file"
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 60380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

EN_FILE="app/src/lib/i18n/en.ts"
keys_file="$(mktemp)"

# Extract ONLY i18n key names (the quoted object keys) that end with a colon.
# Example lines supported:
#   '...': 'Some value'
#   '...':
#       'Some value',
rg -n "'devOptions\.toolPolicyDiagnostics[^']*'\s*:" "$EN_FILE" \
  | sed -E "s/.*'([^']+)'.*/\1/" \
  | sort -u > "$keys_file"

echo "devOptions.toolPolicyDiagnostics key count: $(wc -l < "$keys_file")"

# Enumerate locale/chunk files under app/src/lib/i18n
locale_files_file="$(mktemp)"
find app/src/lib/i18n -type f \( \
  -name 'en.ts' -o -name 'en-*.ts' \
  -o -name 'ar.ts' -o -name 'ar-*.ts' \
  -o -name 'bn.ts' -o -name 'bn-*.ts' \
  -o -name 'de.ts' -o -name 'de-*.ts' \
  -o -name 'es.ts' -o -name 'es-*.ts' \
  -o -name 'fr.ts' -o -name 'fr-*.ts' \
  -o -name 'hi.ts' -o -name 'hi-*.ts' \
  -o -name 'id.ts' -o -name 'id-*.ts' \
  -o -name 'it.ts' -o -name 'it-*.ts' \
  -o -name 'ko.ts' -o -name 'ko-*.ts' \
  -o -name 'pt.ts' -o -name 'pt-*.ts' \
  -o -name 'ru.ts' -o -name 'ru-*.ts' \
  -o -name 'zh-CN.ts' -o -name 'zh-CN-*.ts' \
\) | sort > "$locale_files_file"

mapfile -t locale_files < "$locale_files_file"
echo "Locale/chunk files found: ${`#locale_files`[@]}"

missing_report_file="$(mktemp)"
missing_any=0

# Compare by presence of the exact key token "'<key>'"
while IFS= read -r f; do
  missing_keys_file="$(mktemp)"
  while IFS= read -r k; do
    token="'$k'"
    if ! rg -nF "$token" "$f" >/dev/null; then
      echo "$k" >> "$missing_keys_file"
    fi
  done < "$keys_file"

  if [[ -s "$missing_keys_file" ]]; then
    missing_any=1
    {
      echo "MISSING in $f:"
      # keep output bounded
      head -n 40 "$missing_keys_file"
      if [[ $(wc -l < "$missing_keys_file") -gt 40 ]]; then
        echo "  ... (and $(( $(wc -l < "$missing_keys_file") - 40 )) more)"
      fi
      echo
    } >> "$missing_report_file"
  fi

  rm -f "$missing_keys_file"
done < "$locale_files_file"

if [[ $missing_any -eq 0 ]]; then
  echo "✅ All locale/chunk files contain every new devOptions.toolPolicyDiagnostics key."
else
  echo "❌ Missing keys detected (showing up to 40 per file):"
  cat "$missing_report_file"
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 45495


🏁 Script executed:

#!/bin/bash
set -euo pipefail

EN_FILE="app/src/lib/i18n/en.ts"
CHUNKS_DIR="app/src/lib/i18n/chunks"

locales=(ar bn de es fr hi id it ko pt ru zh-CN)

# Extract all i18n keys under devOptions.toolPolicyDiagnostics... from en.ts
keys_file="$(mktemp)"
rg -o "'devOptions\.toolPolicyDiagnostics[^']*'" "$EN_FILE" \
  | sed "s/^'//; s/'$//" \
  | sort -u > "$keys_file"

echo "devOptions.toolPolicyDiagnostics key count: $(wc -l < "$keys_file")"

# English chunk files
mapfile -t en_chunk_files < <(find "$CHUNKS_DIR" -type f -name 'en-*.ts' | sort)

missing_total=0
missing_report_file="$(mktemp)"

for key in $(cat "$keys_file"); do
  # Find which en-N.ts contains this key
  matches=()
  for f in "${en_chunk_files[@]}"; do
    if rg -nF "'$key'" "$f" >/dev/null; then
      matches+=("$f")
    fi
  done

  if [[ ${`#matches`[@]} -ne 1 ]]; then
    echo "KEY '$key' appears in ${`#matches`[@]} English chunk files: ${matches[*]:-<none>}" >> "$missing_report_file"
    missing_total=1
    continue
  fi

  en_chunk="${matches[0]}"
  base="$(basename "$en_chunk")" # en-<N>.ts
  N="$(sed -E 's/^en-([0-9]+)\.ts$/\1/' <<< "$base")"

  for loc in "${locales[@]}"; do
    loc_chunk="$CHUNKS_DIR/${loc}-${N}.ts"
    if [[ ! -f "$loc_chunk" ]]; then
      echo "MISSING file: $loc_chunk (for key '$key')" >> "$missing_report_file"
      missing_total=1
      continue
    fi
    if ! rg -nF "'$key'" "$loc_chunk" >/dev/null; then
      echo "MISSING key '$key' in $loc_chunk" >> "$missing_report_file"
      missing_total=1
    fi
  done
done

if [[ $missing_total -eq 0 ]]; then
  echo "✅ All devOptions.toolPolicyDiagnostics keys are present in the corresponding en-N.ts and in every locale's ${loc}-N.ts chunk."
else
  echo "❌ Missing i18n chunk parity:"
  # limit output
  head -n 120 "$missing_report_file"
  echo "... (see full report in $missing_report_file)"
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 189


🏁 Script executed:

#!/bin/bash
set -euo pipefail

EN_FILE="app/src/lib/i18n/en.ts"
CHUNKS_DIR="app/src/lib/i18n/chunks"
locales=(ar bn de es fr hi id it ko pt ru zh-CN)

# Extract devOptions.toolPolicyDiagnostics keys from en.ts:
# Capture the quoted key BEFORE the colon (not the value).
keys_file="$(mktemp)"
rg -n "'devOptions\.toolPolicyDiagnostics[^']*'\s*:" "$EN_FILE" \
  | sed -E "s/^.*'([^']+)'\s*:\s*.*$/\1/" \
  | sort -u > "$keys_file"

echo "devOptions.toolPolicyDiagnostics key count: $(wc -l < "$keys_file")"

# List English chunk files (en-<N>.ts)
en_chunk_files_file="$(mktemp)"
find "$CHUNKS_DIR" -type f -name 'en-*.ts' | sort > "$en_chunk_files_file"
mapfile -t en_chunk_files < "$en_chunk_files_file"
echo "English chunk files: ${`#en_chunk_files`[@]}"

missing_any=0
missing_report_file="$(mktemp)"

while IFS= read -r key; do
  # Find the en-<N>.ts that contains this key
  matches_file="$(mktemp)"
  for f in "${en_chunk_files[@]}"; do
    if rg -nF "'$key'" "$f" >/dev/null; then
      echo "$f" >> "$matches_file"
    fi
  done
  matches_count=$(wc -l < "$matches_file")

  if [[ $matches_count -ne 1 ]]; then
    missing_any=1
    {
      echo "KEY present in $matches_count English chunk files:"
      sed 's/^/  - /' "$matches_file"
      echo "  key: $key"
      echo
    } >> "$missing_report_file"
    rm -f "$matches_file"
    continue
  fi

  en_chunk="$(cat "$matches_file")"
  rm -f "$matches_file"

  base="$(basename "$en_chunk")" # en-<N>.ts
  N="$(sed -E 's/^en-([0-9]+)\.ts$/\1/' <<< "$base")"

  for loc in "${locales[@]}"; do
    loc_chunk="$CHUNKS_DIR/${loc}-${N}.ts"
    if [[ ! -f "$loc_chunk" ]]; then
      missing_any=1
      echo "MISSING file: $loc_chunk (for key $key)" >> "$missing_report_file"
      continue
    fi
    if ! rg -nF "'$key'" "$loc_chunk" >/dev/null; then
      missing_any=1
      echo "MISSING key '$key' in $loc_chunk" >> "$missing_report_file"
    fi
  done
done < "$keys_file"

if [[ $missing_any -eq 0 ]]; then
  echo "✅ All devOptions.toolPolicyDiagnostics keys exist in corresponding en-<N>.ts and every ${locales[*]}-<N>.ts"
else
  echo "❌ Missing i18n chunk parity:"
  head -n 80 "$missing_report_file"
  echo "... (see full report in $missing_report_file)"
  exit 2
fi

Repository: tinyhumansai/openhuman

Length of output: 2583


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "devOptions\.toolPolicyDiagnostics\.inventory\.title" app/src/lib/i18n/chunks/en-*.ts || true
rg -n "devOptions\.toolPolicyDiagnostics\.mcpAllowlists\.summary" app/src/lib/i18n/chunks/en-*.ts || true
rg -n "devOptions\.toolPolicyDiagnostics\.inventory\.title" app/src/lib/i18n/chunks/ar-*.ts || true

# Also check en.ts for sanity
rg -n "devOptions\.toolPolicyDiagnostics\.inventory\.title" app/src/lib/i18n/en.ts || true

Repository: tinyhumansai/openhuman

Length of output: 137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

keys=(
  "devOptions.toolPolicyDiagnostics.inventory.title"
  "devOptions.toolPolicyDiagnostics.loading"
  "devOptions.toolPolicyDiagnostics.mcpAllowlists.summary"
  "devOptions.toolPolicyDiagnostics.redactedSurfaces.title"
)

for k in "${keys[@]}"; do
  echo "---- $k in en chunks"
  rg -n "$k" app/src/lib/i18n/chunks/en-*.ts || true
done

echo "---- sanity check: same keys in ar chunks"
for k in "${keys[@]}"; do
  rg -n "$k" app/src/lib/i18n/chunks/ar-*.ts || true
done

Repository: tinyhumansai/openhuman

Length of output: 381


Propagate the new devOptions.toolPolicyDiagnostics.* i18n keys into all chunked locale files

The keys added in app/src/lib/i18n/en.ts (devOptions.toolPolicyDiagnostics.*) are missing from app/src/lib/i18n/chunks/en-*.ts and corresponding non-English chunk files (e.g., ar-*.ts), which will cause missing UI text for chunked locales. Add these exact keys to the matching en-N.ts chunk and to every non-English locale chunk (ar, bn, de, es, fr, hi, id, it, ko, pt, ru, zh-CN) using the English value as the placeholder if needed.

🤖 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/en.ts` around lines 516 - 543, The new i18n keys under
devOptions.toolPolicyDiagnostics (e.g.,
devOptions.toolPolicyDiagnostics.posture.autonomy, .inventory.totalTools,
.mcpAllowlists.summary, etc.) were added to app/src/lib/i18n/en.ts but not
propagated to the chunked locale files; update the matching en chunk file
(en-*.ts) and every non-English locale chunk (ar, bn, de, es, fr, hi, id, it,
ko, pt, ru, zh-CN) by adding the exact same key names and using the English
strings as placeholders so the UI won’t show missing text for chunked locales —
search for the devOptions.toolPolicyDiagnostics prefix to locate where to insert
the new keys in each chunk file.

@graycyrus
Copy link
Copy Markdown
Contributor

@MrMrVlad this PR has merge conflicts with main — please rebase/resolve before review.

@graycyrus
Copy link
Copy Markdown
Contributor

This PR has merge conflicts with main — please rebase/resolve before review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tool-policy diagnostics and conformance reporting

2 participants