Files
TheRock/rocm-systems/experimental/python/perfxpert/docs/known-issues.md
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

9.4 KiB

PerfXpert — Known Issues

Codex gate is prompt-layer only

perfxpert-code codex stages the same MCP surface as the other backends, but its gate remains prompt-layer-only. As of April 2026 Codex's native PreToolUse surface intercepts Bash only, not MCP / Write / other tool calls, so it cannot satisfy PerfXpert's "block every non-perfxpert tool until intent_classify returns" contract.

Current backend split:

  • Patched opencode path — mechanical gate via fork patch 0020 ({block, retryWith} in tool.execute.before)
  • Claude Code — mechanical gate via native PreToolUse
  • Gemini CLI — mechanical gate via project-local BeforeTool / AfterTool hooks + runtime lift
  • Codex CLI — prompt-layer rejection language in the perfxpert-managed AGENTS.override.md compatibility override

If you need a hard pre-tool-call gate today, use the default patched opencode path, Claude Code, or Gemini CLI instead of Codex.

LLM end-to-end smoke test may fail with 429 insufficient_quota

tests/test_integration/test_llm_end_to_end.py::test_llm_enabled_produces_rec_type executes a real OpenAI Agents SDK call against gpt-4o-mini (default) or $PERFXPERT_AGENTS_MODEL_OPENAI if set. The test will skip when OPENAI_API_KEY is unset. When the key is set, the wiring is exercised end-to-end: framework._sdk_invoke() builds an agents.Agent, calls Runner.run_sync(...), and extracts final_output + tool-call metadata into a FakeProviderResponse.

If the OpenAI account backing OPENAI_API_KEY has exhausted its quota, the SDK returns 429 insufficient_quota and the test fails (not skips). This is a billing condition, not a code defect: the same error is reproducible via a direct agents.Runner.run_sync(...) call outside pytest, confirming the wiring is live.

Workarounds to confirm the wiring on a different account:

  • Re-run with a different key: OPENAI_API_KEY=sk-... pytest -k test_llm_enabled_produces_rec_type
  • Override the model per-provider: PERFXPERT_AGENTS_MODEL_OPENAI=gpt-3.5-turbo ...
  • Global model override: PERFXPERT_LLM_MODEL=gpt-4o-mini ...
  • Bump runner turn budget: PERFXPERT_AGENTS_MAX_TURNS=5 (default 10)

This entry will be removed once a CI-owned key with guaranteed quota is provisioned.

Historical: LLM payload field-name mismatch (obsolete — rocm-systems#4979)

Status: obsolete. No fix required on perfxpert.

In the pre-refactor codebase, the now-deleted bridge function ai_analysis/api.py::_convert_result_to_llm_format() emitted kernel dictionaries with the keys calls and percent_of_total, but the consumer ai_analysis/llm_analyzer.py::_sanitize_data() expected dispatch_count and pct_total_time. Memory directions also leaked as verbose labels (Host-to-Device) instead of compact IDs (h2d, d2h, d2d). The effect was that the LLM received None for every kernel metric — silent data loss, not a crash.

Upstream PR rocm-systems#4979 added _MEMORY_DIR_MAP plus field-name renames inside ai_analysis/api.py. It was never merged.

Why the bug cannot occur in perfxpert

The agentic refactor deleted the entire perfxpert/ai_analysis/ package — both sides of the mismatched bridge are gone. The current flow is producer-consumer symmetric by construction:

  • Producer: perfxpert/analysis/core.py::identify_hotspots() emits kernel dicts with keys calls and percent_of_total.
  • Consumers: perfxpert/analysis/recommendations.py, perfxpert/agents/analysis.py, and downstream formatters read the SAME vocabulary (calls, percent_of_total, api_calls). There is no _sanitize_data/_format_data_for_llm translation step to drift.
  • LLM invocation: agents/framework.py::_sdk_invoke() serialises the agent payload as OpenAI-style messages=[{"role", "content"}]; there is no bespoke field-mapping layer between perfxpert's internal dicts and the provider API.

This note is preserved for institutional memory so future contributors who find PR #4979 in the commit history understand why it was closed without being ported.

Ship state (cycle-3 convergence, 2026-04-20)

  • Cycle-3 reviewers: 0 blockers, 0 important across all three branches.
  • Test suite: 1383 passed / 3 skipped / 0 failed (measured 2026-04-20 after Phase 8 LLM provider routing fix). Skips are documented opencode-binary absences (2) plus test_llm_end_to_end.py skip-on-429/auth/transient (1). The Phase 8 delta added 3 tests in test_agents/test_framework.py covering LitellmModel wiring for anthropic / plain-openai / double-prefix guards.
  • Secret scanning: local-only dev tool; not shipped in the repo. Each developer is responsible for their own secret-detection tooling. The scanner, its CI workflow, pre-commit hook, and contributor guide were removed on 2026-04-19.
  • Known ongoing work (not blocking ship):
    • LLM E2E rec_type assertion requires a live key with quota; use OPENAI_API_KEY or ANTHROPIC_API_KEY and a model on-roster.
    • Confluence update remediation: see docs/operations/confluence-publish.md for the manual update recipe; automatic MCP publish requires Atlassian URL + token env vars.

Opencode fork / bundling

  • Patch application is wired into the current build/install path. The setup/build flow applies .patches/*.patch to the pinned opencode submodule and builds the patched binary automatically when prerequisites are present. perfxpert-code install-patches is the rebuild helper for source/editable checkouts; packaged installs prefer the bundled result.

  • The opencode submodule (.gitmodules pin v1.4.11) is MIT. All customizations are carried in .patches/*.patch. Do NOT commit mutations inside the submodule; the submodule's committed state must stay pristine so that git submodule update can fetch upstream fixes.

  • Rate-limit escape hatch is opencode-process-scoped. Setting PERFXPERT_DISABLE_RATE_LIMIT_RETRY=1 kills client-side retries in the opencode process, but the provider's own quota enforcement is external and not affected. Use PERFXPERT_LLM_FALLBACK_CHAIN to cascade across providers when rate-limited.

  • Upstream-opencode fallback weakens the opencode gate. If you force PERFXPERT_OPENCODE_PATH to an upstream opencode binary, or fall through to one on PATH, the fork-only {block, retryWith} behavior is unavailable. The launcher warns in that mode and falls back to prompt guidance only.

Docs-audit baseline

Tracks docs-audit gaps that cannot be mechanically fixed by the scanners but are known and tolerated for now. Each entry has a one-line rationale + optional follow-up tracking id.

Zero-violation baseline

None. All three scanners (scripts/lint.sh, scripts/link-checker.py, scripts/test-samples.py) report zero violations live, enforced by tests/test_docs_tooling/test_ship_readiness.py — which runs each scanner in --strict mode and asserts rc == 0. Green test = zero violations today; no frozen JSON snapshot is kept in the repo.

Scanner scope limitations

Documented here so users reading "zero violations" know what is and isn't covered.

  • External URLs not validated. Any http:// or https:// link is skipped (is_external_url). Dead external links will not flag.
  • Anchor fragments not validated. #section-id is stripped before the file-existence check. A link pointing at a missing anchor inside a real file passes.
  • --strict is output-format only. It suppresses the human-readable preamble and only emits CSV rows; it does NOT enable stricter checks. The set of validated link classes is identical in both modes.

Workaround: rely on Markdown preview in your IDE / GitHub for anchor correctness; external URL health is covered nightly by a separate link-health workflow (not part of the zero-violation baseline).

scripts/lint.sh — banned-string scanner

The banned-string scan excludes these paths (lint.sh:50-54) so that historical context or pre-existing test fixtures don't cause false positives:

  • **/.git/** — git internals
  • **/.pytest_cache/** — test runner cache
  • **/perfxpert/ai_analysis/** — legacy module removed during the agentic refactor; banned terms inside historical fixtures are not live.

The scanner also ignores individual hits whose line contains the historical-anchor phrase "removed in Phase 7.1"; this lets us keep a searchable record of removed flags, env vars, and classes (e.g. in CHANGELOG.md) without re-introducing live guidance.

Consequence: banned strings inside the excluded paths (or on lines carrying the historical anchor) will NOT trip the scanner. If you're adding a new fixture directory that should be ignored for legitimate reasons, add it here with a one-line comment.

Out-of-scope follow-ups

  • tests/test_docs_tooling/test_secret_scanner.py — three tests fail locally because they cd into a fresh tempfile.TemporaryDirectory() without symlinking tools/_secret_scanner.py first. Pre-existing bug (commit c2c419ff9e).
    • Reproducer: pytest tests/test_docs_tooling/test_secret_scanner.py -q
    • Owner / tracking: queued in a follow-up sweep; no issue number assigned yet. Fix is a one-line tmpdir setup (copy or symlink tools/_secret_scanner.py into the tmpdir before the subprocess call).
    • Workaround for affected devs: skip the three test_secret_scanner_* tests with -k 'not secret_scanner' while the fix is queued; they don't gate any other scanner.