Skip to content

fix(api-wiring): resolve frontend/backend contract drift (audit #9851) - #9862

Merged
mrveiss merged 9 commits into
Dev_new_guifrom
api-wiring-fixes
Jun 10, 2026
Merged

mrveiss merged 9 commits into
Dev_new_guifrom
api-wiring-fixes

Conversation

@mrveiss

@mrveiss mrveiss commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Closes part of #9851. Follow-ups tracked in #9861.

Thinking Path

The API wiring audit (authoritative, 2055-path OpenAPI dump) found 70 unwired frontend calls + 3 unmounted routers. Root-caused and fixed the real drift; deferred items that need backend implementation or a frontend redesign rather than patching blind. One commit per logical fix.

What Changed

Backend

  • codebase_analytics router silently unmounted (2 import-chain type bugs: int-typed embedding-mode .lower(), builtin callable | None eager eval) → now mounts 81 paths, resolving ~39 dead /api/analytics/codebase/* calls.
  • user_provider_credentials mounted (was unregistered + importing phantom auth_middleware deps); added a get_current_user_id UUID dependency.
  • Transcriber double-prefix removed (/api/api/transcriber/* dead duplicate).
  • Two minimal LLC reads: GET /api/llc/heartbeat-runs, GET /api/llc/work-items/{id}/activity (tenant-isolated).

Frontend

  • LLC views (CompanyDashboard, WorkItemDetail, GoalTree) → canonical routes, dropped the non-existent {data:{...}} envelope.
  • Device pairing panel → canonical mobile_devices.py (QR-challenge flow); removed the dead confirm POST.

CI

  • .github/workflows/api-wiring.yml (non-blocking until known FPs cleared).

Result: unwired 70→19 (14 false positives, 5 deferred → #9861); unmounted 3→2 (both verified-mounted FPs). Full write-up in WIRING_AUDIT_REPORT.md.

Verification

  • app.openapi() builds: 2055 paths (all changed backend modules import cleanly).
  • LLC unit tests: test_activity_log + test_work_item — 30 passed.
  • Authoritative audit re-run confirms 70→19.
  • IDOR in the new /activity endpoint (caught by automated commit review) fixed before merge — tenant scope from require_org_context.
  • Types: gen:types must run against a fully-provisioned backend (constrained-env regen was lossy for 77 endpoints; not committed).

Model Used

Claude Opus 4.8 (1M context)

mrveiss and others added 8 commits June 10, 2026 09:19
…bugs (#9851)

The entire codebase_analytics router (81 routes under /api/analytics/codebase/*)
was silently unmounted: feature_routers.py swallowed two import-time crashes,
leaving ~39 frontend calls hitting 404s.

Root causes:
- ssot_config.codebase_index_embedding_mode declared `int` (default 0) but the
  only consumer (chromadb_storage.py) calls `.lower()` → AttributeError. Retyped
  to `str` (default "precompute"), matching the documented "precompute|auto|skip".
- scanner.py annotated `progress_callback: callable | None` using the builtin
  `callable`; PEP-604 unions evaluate eagerly without `from __future__ import
  annotations`, so `callable | None` raised TypeError at import. Use typing.Callable.

Verified: `import api.codebase_analytics` → 84 routes; OpenAPI dump 1984 → 2065
paths; audit unwired 70 → 31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…9851)

api/user_provider_credentials.py (GH#9037) was never registered and could not
have imported: it pulled get_current_user_id and get_db_session from
auth_middleware, neither of which exists there. Wired it in correctly:

- Router prefix /api/user/provider-credentials → /user/provider-credentials
  (app_factory prepends /api, so the literal prefix double-counted).
- Import get_db_session + new get_current_user_id from
  api.user_management.dependencies (the canonical home; get_db_session already
  503s outside multi-user/postgres, so this user-scoped feature is gated there).
- Added get_current_user_id: resolves the authenticated user's UUID from
  id/user_id/sub, 401 for service/internal principals with no real user id.
- Registered in core_routers.

Mounts /api/user/provider-credentials (POST/GET) + /{provider_name} (DELETE).
No frontend consumer yet (GH#9037 UI pending) — wired per "never silently
delete; wire it in".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…scriber (#9851)

Transcriber routes were registered twice: core_routers mounts
api.transcriber.router (prefix "/transcriber" → /api/transcriber/*, the full
surface incl. kb), and feature_routers additionally mounted
extensions.builtin.transcriber_extension whose combined router carries a full
"/api/transcriber" prefix. Since app_factory prepends "/api", the second
registration produced a dead, double-prefixed duplicate at
/api/api/transcriber/* (14 phantom paths). Removed the feature_routers entry;
the core registration is canonical and ungated, so no behavior is lost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nts (#9851)

Two LLC dashboard calls hit non-existent backends. Both are thin reads over
existing models/services, so implemented rather than feature-flagged:

- GET /api/llc/heartbeat-runs — org-scoped run feed for CompanyDashboard's
  "recent runs" panel. Mirrors the per-agent list_runs (runs.py) without the
  agent filter; org isolation via require_org_context.
- GET /api/llc/work-items/{id}/activity — per-item audit trail for
  WorkItemDetail. Resolves the item's company_id then queries the existing
  LLCActivityLogService with entity filter (entity_type="work_item"). Reuses
  ActivityLogResponse/Entry from the company-activity route.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#9851)

Several LLC views called non-existent paths and assumed a {data:{...}} axios
envelope the api client doesn't use (it returns parsed JSON directly). Aligned
with the real backend:

- CompanyDashboard: agents/status→agents, approvals/pending→approvals (with
  company_id), budgets→budget, activity→companies/{id}/activity (page_size);
  approvals/{id}/approve→/decide {decision:"approved"}; unwrapped array/items.
  heartbeat-runs now resolves (new backend endpoint).
- WorkItemDetail: artifacts→products (reads {products}); activity reads {items}
  (ActivityLogResponse) — endpoint now exists.
- GoalTree: goals/{id}/items→/tasks, returns an array directly.

Note: a separate pre-existing defect remains — CompanyDashboard et al. read
route.params.companyId on routes that define no such param (no company store),
so company context is unresolved. Tracked in the umbrella (#9851) for a focused
fix; these path/shape corrections are prerequisites, not regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…9851)

DevicePairingSettingsPanel called three non-existent routes and assumed an
axios {data} envelope. mobile_devices.py is canonical:

- loadPairedDevices: GET /api/devices/paired → GET /api/devices ({devices:[...]}).
- startPairingFlow: POST /api/devices/pairing/generate-code → GET
  /api/devices/pair-qr ({challenge_token}); render the token as the QR.
- completePairing: removed the dead POST /api/devices/pairing/confirm — pairing
  completes mobile-side (the device POSTs the scanned token to /api/devices/pair);
  the desktop confirms by refreshing the device list. No dead button.

ApiClient returns parsed JSON directly, so .data dereferences were dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs scripts/audit_api_wiring.py on PRs touching backend routes or frontend
src: builds the app + dumps app.openapi() (authoritative), falling back to
static regex mode if the build fails in CI. Non-blocking for now
(continue-on-error) since known false positives remain — template-literal
paths, bare base-URL strings, websockets (absent from OpenAPI), and LLC views
pending a company-context redesign (#9851). Remove continue-on-error to make it
a blocking gate once those are cleared.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on (#9851)

Records what was fixed (codebase_analytics mount, user-creds, transcriber dedup,
2 minimal LLC endpoints, LLC + device-pairing frontend), the IDOR caught in
review, deferred items (tracked in #9861), and known audit false positives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

Applied Black, isort, and autoflake to match code-quality checks.
Triggered by workflow auto-fix-formatting.yml.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@mrveiss

mrveiss commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

✅ Ready to merge

Comprehensive API wiring remediation:

  • Fixed 3 silently unmounted routers (codebase_analytics type bugs, user_provider_credentials phantom deps, transcriber double-prefix)
  • Aligned frontend calls with canonical routes (LLC views, device pairing)
  • Added CI workflow to prevent future drift
  • Verification complete: app.openapi() builds (2055 paths), LLC tests pass (30), audit 70→19 unwired
  • IDOR in new /activity endpoint fixed (tenant scope via require_org_context)

CI green. Merging.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant