Platform Parity Auditor
Enforce feature parity and correct layer placement across Foundry's multi-platform monorepo. Prevents drift between apps/web/ (Next.js) and apps/desktop/ (Tauri/Vite/React), and ensures shared packages (packages/ui/, packages/types/) are used correctly.
Target structure (from docs/plans/bun_workspace_tauri_implementation_plan.md):
Foundry-App/
convex/ # Stays at root
packages/
types/ # @foundry/types — shared type definitions
ui/ # @foundry/ui — shared sandbox HUD components
src/sandbox/backend.ts # ISandboxBackend interface
apps/
web/ # Next.js 15 + App Router
desktop/ # Tauri 2 + Vite + React 19
sandbox-worker/
agent-service/
Audit Modes
When invoked, ask the user which mode to run (or run all if they say "full audit"):
1. Drift Audit
Scan both platforms for parallel implementations that should be shared.
Run these searches:
Glob: apps/web/src/components/**/*.tsx
Glob: apps/desktop/src/components/**/*.tsx
Compare filenames across both trees. Flag:
- Identical names — same component name in both
apps/web/andapps/desktop/(strong candidate for extraction topackages/ui/) - Similar purpose — components with different names but overlapping functionality (e.g.,
WebLogViewervsDesktopLogStream) - Duplicated hooks — custom hooks that exist in both apps
Also check for type duplication:
Grep: "export (type|interface|enum)" in apps/web/src/**/*.ts
Grep: "export (type|interface|enum)" in apps/desktop/src/**/*.ts
Flag any type definitions that exist in both apps but not in packages/types/.
Output: Table of duplicated components/types with recommendation (extract to shared, keep platform-specific, or merge).
2. Layer Placement Check
When reviewing new or modified code, verify it lives in the correct layer.
Rules:
| Code Type | Correct Location | Detection Pattern |
|---|---|---|
| Pure React components (no framework imports) | packages/ui/ |
No imports from next/*, @clerk/nextjs, @tauri-apps/*, react-router-dom |
| Types, validators, constants | packages/types/ |
export type, export interface, export enum, export const with no runtime deps |
| Next.js-coupled code | apps/web/ only |
Imports useRouter from next/navigation, next/link, next/dynamic, @clerk/nextjs, next/image |
| Tauri-coupled code | apps/desktop/ only |
Imports invoke from @tauri-apps/api, @tauri-apps/* |
| Convex queries/mutations | Can live in shared or either app | Imports from convex/react or generated _generated/api |
Run these checks:
# Next.js imports in shared packages (VIOLATION)
Grep: "from ['\"]next/" in packages/**/*.{ts,tsx}
Grep: "from ['\"]@clerk/nextjs" in packages/**/*.{ts,tsx}
# Tauri imports in shared packages (VIOLATION)
Grep: "from ['\"]@tauri-apps" in packages/**/*.{ts,tsx}
# Next.js imports in desktop app (VIOLATION)
Grep: "from ['\"]next/" in apps/desktop/**/*.{ts,tsx}
Grep: "from ['\"]@clerk/nextjs" in apps/desktop/**/*.{ts,tsx}
# Tauri imports in web app (VIOLATION)
Grep: "from ['\"]@tauri-apps" in apps/web/**/*.{ts,tsx}
# Pure React in app-specific dirs (candidate for extraction)
# Look for components with no framework imports
Output: List of violations with file path, import statement, and recommended fix.
3. Abstraction Seam Validation
The three critical interfaces must not be bypassed:
ISandboxBackend
Shared sandbox components in packages/ui/ must use the ISandboxBackend interface, never call Convex actions or Tauri commands directly.
# Direct Convex sandbox calls in shared UI (VIOLATION)
Grep: "useMutation.*sandbox" in packages/ui/**/*.{ts,tsx}
Grep: "useAction.*sandbox" in packages/ui/**/*.{ts,tsx}
Grep: "(api as any)\.sandbox" in packages/ui/**/*.{ts,tsx}
# Direct Tauri invoke in shared UI (VIOLATION)
Grep: "invoke\(" in packages/ui/**/*.{ts,tsx}
Auth Adapter
Shared components must not import auth providers directly.
# Direct Clerk imports in shared packages (VIOLATION)
Grep: "from ['\"]@clerk/nextjs" in packages/**/*.{ts,tsx}
Grep: "from ['\"]@clerk/clerk-react" in packages/**/*.{ts,tsx}
Grep: "useOrganization|useUser|useAuth" in packages/ui/**/*.{ts,tsx}
Shared components should receive auth context via props or a framework-agnostic adapter.
Router Adapter
Shared components must not import routing libraries directly.
# Direct router imports in shared packages (VIOLATION)
Grep: "from ['\"]next/navigation" in packages/**/*.{ts,tsx}
Grep: "from ['\"]next/link" in packages/**/*.{ts,tsx}
Grep: "from ['\"]react-router-dom" in packages/**/*.{ts,tsx}
Shared components should use a NavigationAdapter or receive navigation callbacks as props.
Output: Pass/fail for each seam with specific violations listed.
4. Parallel Implementation Detection
Deep scan for code duplication across platforms.
# Components with identical names
Glob: apps/web/src/components/**/*.tsx → extract basenames
Glob: apps/desktop/src/components/**/*.tsx → extract basenames
# Intersect the two lists
# Similar function signatures
Grep: "export (default )?(function|const) \w+" in apps/web/src/**/*.{ts,tsx}
Grep: "export (default )?(function|const) \w+" in apps/desktop/src/**/*.{ts,tsx}
# Compare exported function names
# Duplicate type definitions
Grep: "export (type|interface) \w+" in apps/web/src/**/*.ts
Grep: "export (type|interface) \w+" in apps/desktop/src/**/*.ts
# Flag any that aren't in @foundry/types
Known framework-agnostic components (from current codebase analysis — strong extraction candidates):
SandboxLogStream— pure log renderingChatPanel— chat UI (needsISandboxBackendfor message sending)SandboxTerminal— terminal emulator (needs WebSocket URL from backend)SandboxStatusBadge— status displayRuntimeModeBadge— runtime mode displayStageProgress— setup stage progress barSandboxFileChanges— file diff viewer
Known platform-coupled components (must stay in their respective apps):
SandboxHUD— usesuseRouterfromnext/navigationSandboxEditor— usesnext/dynamicSandboxManagerPage— usesnext/link,@clerk/nextjsSandboxSettingsPage— usesnext/link,@clerk/nextjs
Output: List of parallel implementations with extraction recommendation.
5. Schema Compatibility Check
Verify convex/schema.ts supports both runtimes where needed.
# Check for runtime field on session tables
Grep: "runtime" in convex/schema.ts
# Expected fields on sandboxSessions:
# runtime: v.optional(v.union(v.literal("cloud"), v.literal("local")))
# localDeviceId: v.optional(v.string())
# localDeviceName: v.optional(v.string())
# Check for runtime-aware indexes
Grep: "by_runtime" in convex/schema.ts
# Check that new Convex functions exist for local execution
Grep: "startLocal|reportLocalCompletion|appendBatchFromDesktop" in convex/sandbox/**/*.ts
Output: Schema readiness checklist with pass/fail per requirement.
Full Audit
When the user requests a "full audit" or "parity check", run all 5 modes sequentially and produce a summary report:
## Platform Parity Report
### Drift Audit
- X components duplicated across platforms
- Y types duplicated (should be in @foundry/types)
### Layer Placement
- X violations found
- [list each violation]
### Abstraction Seams
- ISandboxBackend: PASS/FAIL
- Auth Adapter: PASS/FAIL
- Router Adapter: PASS/FAIL
### Parallel Implementations
- X identical components found
- Y similar function signatures
### Schema Compatibility
- Runtime field: PRESENT/MISSING
- Runtime index: PRESENT/MISSING
- Local execution functions: X/3 found
When to Trigger
Run this skill proactively when:
- Adding a new component or feature that could apply to both platforms
- Moving files between
apps/andpackages/ - Reviewing PRs that touch
packages/ui/orpackages/types/ - After a batch of changes to either platform
- Before a release to verify no drift has accumulated
- When someone asks about "parity", "drift", "platform sync", or "web vs desktop"