Claude Code Plugins

Community-maintained marketplace

Feedback

Create language conversion skills for translating code from language A to language B. Use when building 'convert-X-Y' skills, designing type mappings between languages, establishing idiom translation patterns, or defining conversion methodologies. Provides foundational patterns that specific conversion skills extend.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name meta-convert-dev
description Create language conversion skills for translating code from language A to language B. Use when building 'convert-X-Y' skills, designing type mappings between languages, establishing idiom translation patterns, or defining conversion methodologies. Provides foundational patterns that specific conversion skills extend.

Language Conversion Skill Development

Foundational patterns for creating one-way language conversion skills. This meta-skill guides the creation of convert-X-Y skills (e.g., convert-typescript-rust, convert-python-golang) that assist in translating/refactoring code from a source language to a target language.

When to Use This Skill

  • Creating a new convert-X-Y skill for language translation
  • Designing type system mappings between languages
  • Establishing idiom translation strategies
  • Defining conversion workflows and validation approaches
  • Building tooling recommendations for transpilation

This Skill Does NOT Cover

  • Actual code conversion (use specific convert-X-Y skills or meta-convert-guide)
  • Language tutorials (see lang-*-dev skills)
  • Bidirectional translation (each direction is a separate skill)
  • Runtime interop/FFI (see language-specific interop skills)

Related Skills

  • meta-convert-guide - Patterns and strategies for performing conversions
  • convert-* skills - Language-pair specific conversion skills

Existing Conversion Skills

For concrete, language-pair-specific examples, see these skills:

Skill Description
convert-typescript-rust TypeScript → Rust (GC → ownership, exceptions → Result)
convert-typescript-python TypeScript → Python (static → dynamic typing)
convert-typescript-golang TypeScript → Go (OOP → simplicity, Promise → goroutine)
convert-golang-rust Go → Rust (GC → ownership, interface → trait)
convert-python-rust Python → Rust (dynamic → static, GC → ownership)
convert-python-fsharp Python → F# (dynamic → static, OOP → functional)
convert-python-erlang Python → Erlang (single-threaded → BEAM actors)
convert-python-clojure Python → Clojure (imperative → functional, OOP → data-oriented)
convert-clojure-roc Clojure → Roc (JVM → native, dynamic → static)
convert-clojure-elixir Clojure → Elixir (JVM → BEAM, STM → actors)
convert-clojure-haskell Clojure → Haskell (dynamic → static, practical → pure)

Note: This list may not be complete. Search components/skills/convert-* for all available conversion skills.

Skill Categories

Category Examples Key Challenges
Static → Static TypeScript→Go, Rust→Go Type system mapping, idiom differences
Dynamic → Static Python→Rust, Clojure→Haskell Add types, handle runtime flexibility
Static → Dynamic TypeScript→Python, Go→Elixir Remove type annotations, embrace flexibility
Dynamic → Dynamic Python→Clojure, Clojure→Elixir Paradigm and runtime differences
OOP → Functional Java→Clojure, Python→Haskell Replace classes with data+functions
Functional → Functional Clojure→Elixir, Haskell→Scala FP dialect differences
GC → Ownership Any→Rust Add explicit lifetimes and borrowing
Platform Migration JVM→BEAM, JVM→Native Runtime semantics, library ecosystem

When creating a new conversion skill, refer to the specific convert-X-Y skills for production-ready examples.

Platform Ecosystem Considerations

When converting between languages on different platforms (JVM, BEAM, .NET, Native), consider:

Platform Key Characteristics
JVM Bytecode, JIT, GC, thread-based concurrency, rich stdlib
BEAM Lightweight processes, preemptive scheduling, fault tolerance, hot reload
.NET Similar to JVM, async/await primitives, strong Windows integration
Native Direct compilation, manual/ownership memory, no runtime overhead
Scripting Interpreted/JIT, dynamic typing, GIL (Python)

See: Platform Ecosystem Reference for detailed runtime characteristics, stdlib mapping strategies, transpiler options, and FFI considerations.

Paradigm Translation Patterns

Cross-paradigm conversions require mental model shifts:

Source → Target Key Transformation
OOP → FP Classes → data + functions, mutation → immutable updates
Imperative → Declarative Loops → higher-order functions, statements → expressions
Dynamic → Static Add type annotations, handle runtime flexibility statically
Mutable-first → Immutable-first State threading, persistent data structures

See: Paradigm Translation Reference for detailed patterns and examples.

Additional Reference Materials

Topic Reference File
Numeric edge cases references/numeric-edge-cases.md - Overflow handling, division semantics, float precision
Stdlib mapping references/stdlib-mapping.md - Finding equivalent functions across languages
Build systems references/build-system-mapping.md - Package managers and project structure
Module systems references/module-system-comparison.md - Import/export patterns

Conversion Skill Naming Convention

convert-<source>-<target>
Component Description Example
convert Fixed prefix convert-
<source> Source language (lowercase) typescript, python, golang
<target> Target language (lowercase) rust, python, golang

Examples:

  • convert-typescript-rust - TypeScript → Rust
  • convert-python-golang - Python → Go
  • convert-golang-rust - Go → Rust

Note: Each skill is ONE-WAY. convert-A-B and convert-B-A are separate skills with different patterns.


Conversion Skill Structure

Every convert-X-Y skill should follow this structure:

# Convert <Source> to <Target>

## Overview
Brief description of the conversion, common use cases, and what to expect.

## When to Use
- Scenarios where this conversion makes sense
- Benefits of the target language for this use case

## When NOT to Use
- Scenarios where conversion is not recommended
- Better alternatives

## Type System Mapping
Complete mapping table from source types to target types.

## Idiom Translation
Source patterns and their idiomatic target equivalents.

## Error Handling
How to convert error handling patterns.

## Concurrency Patterns
How async/threading models translate.

## Memory & Ownership
If applicable, how memory models differ and translate.

## Testing Strategy
How to verify functional equivalence.

## Tooling
Available tools, transpilers, and validation helpers.

## Common Pitfalls
Mistakes to avoid during conversion.

## Examples
Concrete before/after conversion examples.

The 8 Pillars Framework

Every conversion skill should address these 8 pillars for comprehensive coverage:

Pillar What to Document
1. Module System Import/export, visibility, package structure
2. Error Handling Exception/Result/Option patterns, error propagation
3. Concurrency Async/await, threads, processes, channels
4. Metaprogramming Macros, decorators, reflection, code generation
5. Zero/Default Values Null handling, default initialization, optional types
6. Serialization JSON, binary formats, validation patterns
7. Build System Package managers, build tools, project structure
8. Testing Test frameworks, property-based testing, verification

9th Pillar: Dev Workflow (for REPL-centric languages)

For conversions involving REPL-centric languages (Clojure, Elixir, Erlang, Haskell, Lisp, F#), add:

Pillar What to Document
9. Dev Workflow & REPL REPL patterns, hot reload, interactive debugging

See meta-convert-guide for detailed guidance on each pillar.


Skill Creation Workflow

When creating a new convert-X-Y skill:

1. Assess the Conversion

  • Check for existing skill: Search components/skills/convert-*
  • Check for reverse skill: If creating convert-A-B, check if convert-B-A exists
  • Identify category: Static→Dynamic, GC→Ownership, etc.
  • Estimate difficulty: See difficulty-matrix.md for pre-calculated ratings

2. Gather Language Knowledge

  • Read lang-X-dev skill for source language
  • Read lang-Y-dev skill for target language
  • Identify key differences in the 8 pillars

3. Create Type Mapping Tables

For each pillar, create comprehensive mapping tables:

| Source (X) | Target (Y) | Notes |
|------------|------------|-------|
| `string`   | `String`   | ...   |

4. Document Idiom Translations

Show idiomatic translations, not literal ports:

// Source: X
[source code]

// Target: Y (idiomatic)
[target code]

// Avoid: Y (transliterated)
[non-idiomatic code]

5. Include Testing Strategy

  • Port existing tests first
  • Add property-based tests for invariants
  • Use golden testing for output comparison

6. Self-Review Checklist

Before finalizing the skill:

  • All 8 pillars addressed (9 if REPL-centric)
  • Type mappings are complete
  • Examples are idiomatic, not transliterated
  • Common pitfalls documented
  • Testing strategy included
  • Cross-references to related skills

Concurrency Pattern Translation

When converting code between languages, concurrency models are often the most challenging aspect. Different languages embody fundamentally different concurrency philosophies, from shared-memory threads to isolated processes, from promises to channels, from Software Transactional Memory (STM) to actor models.

This section provides a comprehensive guide to translating concurrency patterns across languages.

Concurrency Model Matrix

Understanding the mapping between source and target concurrency models is crucial for successful conversion:

Source Model Target Model Translation Strategy Key Considerations
Actors (BEAM) STM (Clojure) Use atoms/refs for state, core.async for message-passing BEAM processes → atoms for simple state, refs+dosync for coordinated updates
Actors (BEAM) IO Monad (Haskell) Use TVar for shared state, async for spawning GenServers → IORef or TVar, supervision → exception handling
Actors (BEAM) Goroutines (Go) Channels for message passing, structs for state GenServer → goroutine with channel, supervisor → error handling
STM (Clojure) Actors (BEAM) Wrap refs in GenServer, use supervisor for coordination dosync transactions → GenServer serializes updates
STM (Clojure) Goroutines (Go) Channels + select for coordination, mutexes for shared state refs → channels or sync.Mutex, atoms → atomic package
Channels (Go) Actors (BEAM) GenStage/Flow for backpressure, GenServer for state Buffered channels → GenServer queue, select → receive
Channels (Go) Promises (JS/TS) Promise wraps channel receive, async/await for coordination Goroutine → async function, channel receive → await
IO Monad (Haskell) Tasks (Roc) Map IO actions to Task effects IO-based concurrency → Task-based effects system
Promises (JS/TS) Goroutines (Go) Goroutines for async execution, channels for communication async function → goroutine, Promise → channel or direct return
Promises (JS/TS) Futures (Rust) Tokio runtime for async, futures for lazy evaluation Promise (eager) → Future (lazy), await → .await
Threads (Python) Actors (BEAM) Spawn processes for each thread, message passing for communication GIL limitations → true parallelism, threading.Thread → spawn
Asyncio (Python) Async/Await (Rust) Tokio runtime, async functions, futures Event loop → runtime, coroutines → async fn

Key Insight: Most conversions require not just syntactic translation but a fundamental shift in how you think about concurrency. For example:

  • Shared memory (threads) → Message passing (actors): Stop thinking about locks; start thinking about process isolation
  • Eager execution (Promises) → Lazy evaluation (Futures): Understand when work actually starts
  • Preemptive scheduling (OS threads) → Cooperative scheduling (async/await): Be aware of blocking operations

Pattern Translation Examples

Example 1: Clojure STM → Elixir Actors (Coordinated State Updates)

Problem: Clojure's STM allows multiple refs to be updated atomically in a transaction. Elixir's actor model has isolated processes.

Clojure (Source):

(def account-a (ref 1000))
(def account-b (ref 500))

(defn transfer [from to amount]
  (dosync
    (alter from - amount)
    (alter to + amount)))

;; Atomic bank transfer
(transfer account-a account-b 100)
;; account-a: 900, account-b: 600

Elixir (Target - Idiomatic Translation):

defmodule Bank do
  use GenServer

  # Client API
  def start_link(initial_accounts) do
    GenServer.start_link(__MODULE__, initial_accounts, name: __MODULE__)
  end

  def transfer(from_account, to_account, amount) do
    GenServer.call(__MODULE__, {:transfer, from_account, to_account, amount})
  end

  def get_balance(account) do
    GenServer.call(__MODULE__, {:get_balance, account})
  end

  # Server Callbacks
  @impl true
  def init(initial_accounts) do
    {:ok, initial_accounts}
  end

  @impl true
  def handle_call({:transfer, from, to, amount}, _from, accounts) do
    case accounts do
      %{^from => from_balance, ^to => to_balance} when from_balance >= amount ->
        updated_accounts = accounts
        |> Map.update!(from, &(&1 - amount))
        |> Map.update!(to, &(&1 + amount))
        {:reply, :ok, updated_accounts}

      _ ->
        {:reply, {:error, :insufficient_funds}, accounts}
    end
  end

  @impl true
  def handle_call({:get_balance, account}, _from, accounts) do
    {:reply, Map.get(accounts, account, 0), accounts}
  end
end

# Usage
{:ok, _pid} = Bank.start_link(%{account_a: 1000, account_b: 500})
:ok = Bank.transfer(:account_a, :account_b, 100)
# account_a: 900, account_b: 600

Translation Strategy:

  1. Centralize coordinated state: All accounts in single GenServer (serializes transactions)
  2. Transaction semantics: GenServer's synchronous call provides atomicity
  3. Pattern matching: Use guards to validate sufficient funds before update
  4. Immutable updates: Map.update! returns new map, pattern matches for validation

Edge Cases & Gotchas:

  • Deadlock prevention: Single GenServer eliminates distributed deadlocks
  • Scalability tradeoff: STM allows concurrent reads; GenServer serializes all operations
  • Alternative: For high throughput, shard accounts across multiple GenServers with distributed coordination

Performance Implications:

  • Clojure STM: Optimistic concurrency (retry on conflict), parallel reads
  • Elixir GenServer: Pessimistic serialization, all operations sequential
  • For read-heavy workloads, consider Agent for each account + coordinator for transfers

Cross-reference: See convert-clojure-elixir for complete STM → Actor translation patterns.


Example 2: Go Channels → TypeScript Promises (Backpressure-Aware Pipeline)

Problem: Go channels provide natural backpressure through blocking sends/receives. JavaScript Promises execute eagerly and don't support backpressure natively.

Go (Source):

func processStream(items []string) <-chan string {
    out := make(chan string, 10) // Buffered channel

    go func() {
        defer close(out)
        for _, item := range items {
            // Simulate slow processing
            time.Sleep(100 * time.Millisecond)
            processed := strings.ToUpper(item)
            out <- processed // Blocks if buffer full (backpressure!)
        }
    }()

    return out
}

func main() {
    results := processStream([]string{"a", "b", "c", "d", "e"})

    for result := range results {
        fmt.Println("Got:", result)
        time.Sleep(200 * time.Millisecond) // Slow consumer
    }
}

TypeScript (Target - Idiomatic Translation):

async function* processStream(items: string[]): AsyncGenerator<string> {
  for (const item of items) {
    // Simulate slow processing
    await new Promise(resolve => setTimeout(resolve, 100));
    const processed = item.toUpperCase();
    yield processed; // Yields control back to consumer
  }
}

async function main() {
  const items = ["a", "b", "c", "d", "e"];

  for await (const result of processStream(items)) {
    console.log("Got:", result);
    await new Promise(resolve => setTimeout(resolve, 200)); // Slow consumer
  }
}

main();

Alternative (Explicit Promise Queue with Backpressure):

class Channel<T> {
  private queue: T[] = [];
  private waiting: ((value: T) => void)[] = [];
  private maxSize: number;

  constructor(bufferSize: number = 10) {
    this.maxSize = bufferSize;
  }

  async send(value: T): Promise<void> {
    // Backpressure: wait if queue is full
    while (this.queue.length >= this.maxSize) {
      await new Promise(resolve => setTimeout(resolve, 10));
    }

    if (this.waiting.length > 0) {
      const resolve = this.waiting.shift()!;
      resolve(value);
    } else {
      this.queue.push(value);
    }
  }

  async receive(): Promise<T> {
    if (this.queue.length > 0) {
      return this.queue.shift()!;
    }

    return new Promise<T>(resolve => {
      this.waiting.push(resolve);
    });
  }
}

async function processStreamWithChannel(items: string[]): Promise<Channel<string>> {
  const ch = new Channel<string>(10);

  (async () => {
    for (const item of items) {
      await new Promise(resolve => setTimeout(resolve, 100));
      await ch.send(item.toUpperCase());
    }
  })();

  return ch;
}

Translation Strategy:

  1. Async generators: Most idiomatic for streaming data in TypeScript
  2. Yield control: yield allows consumer to control pace (like channel receive blocking)
  3. Custom Channel class: For explicit buffering and backpressure semantics
  4. Event-driven alternative: Use Node.js streams for larger data pipelines

Edge Cases & Gotchas:

  • No native backpressure: Promises execute eagerly; must implement queue manually
  • Memory growth: Without backpressure, fast producer overwhelms slow consumer
  • Cancellation: Go channels close; async generators must handle cleanup explicitly
  • Error propagation: Go errors via multiple returns; TypeScript uses try/catch or rejected promises

Performance Implications:

  • Go channels: OS-level blocking, true concurrency across cores
  • TypeScript async: Event loop, single-threaded unless using worker threads
  • For CPU-bound work, consider worker threads or external processing

Cross-reference: See convert-typescript-golang and convert-golang-rust for channel translation patterns.


Example 3: Python Asyncio → Erlang Processes (Event Loop → Actor Model)

Problem: Python's asyncio uses a single-threaded event loop with coroutines. Erlang uses lightweight processes with true preemptive concurrency.

Python (Source):

import asyncio
from typing import Dict

class ConnectionPool:
    def __init__(self, max_connections: int = 10):
        self.max_connections = max_connections
        self.active_connections: Dict[str, asyncio.Task] = {}
        self.semaphore = asyncio.Semaphore(max_connections)

    async def connect(self, conn_id: str):
        async with self.semaphore:
            print(f"Connecting {conn_id}")
            await asyncio.sleep(1)  # Simulate connection
            self.active_connections[conn_id] = asyncio.current_task()
            print(f"Connected {conn_id}")
            return conn_id

    async def disconnect(self, conn_id: str):
        if conn_id in self.active_connections:
            print(f"Disconnecting {conn_id}")
            del self.active_connections[conn_id]

async def main():
    pool = ConnectionPool(max_connections=3)

    # Start 5 connections (only 3 concurrent due to semaphore)
    tasks = [pool.connect(f"conn-{i}") for i in range(5)]
    await asyncio.gather(*tasks)

asyncio.run(main())

Erlang (Target - Idiomatic Translation):

-module(connection_pool).
-behaviour(gen_server).

%% API
-export([start_link/1, connect/2, disconnect/2, stop/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-record(state, {
    max_connections :: integer(),
    active_connections :: #{binary() => pid()},
    waiting_queue :: queue:queue()
}).

%% Client API
start_link(MaxConnections) ->
    gen_server:start_link(?MODULE, MaxConnections, []).

connect(PoolPid, ConnId) ->
    gen_server:call(PoolPid, {connect, ConnId}, infinity).

disconnect(PoolPid, ConnId) ->
    gen_server:cast(PoolPid, {disconnect, ConnId}).

stop(PoolPid) ->
    gen_server:stop(PoolPid).

%% Server Callbacks
init(MaxConnections) ->
    State = #state{
        max_connections = MaxConnections,
        active_connections = #{},
        waiting_queue = queue:new()
    },
    {ok, State}.

handle_call({connect, ConnId}, From, State) ->
    #state{
        max_connections = Max,
        active_connections = Active,
        waiting_queue = Queue
    } = State,

    case maps:size(Active) < Max of
        true ->
            %% Spawn connection process
            Pid = spawn_link(fun() -> connection_worker(ConnId) end),
            io:format("Connecting ~s~n", [ConnId]),
            NewActive = maps:put(ConnId, Pid, Active),
            {reply, {ok, ConnId}, State#state{active_connections = NewActive}};

        false ->
            %% Queue is full, add to waiting queue
            NewQueue = queue:in({ConnId, From}, Queue),
            {noreply, State#state{waiting_queue = NewQueue}}
    end.

handle_cast({disconnect, ConnId}, State) ->
    #state{
        active_connections = Active,
        waiting_queue = Queue
    } = State,

    io:format("Disconnecting ~s~n", [ConnId]),
    NewActive = maps:remove(ConnId, Active),

    %% Process waiting queue
    case queue:out(Queue) of
        {{value, {NextConnId, From}}, NewQueue} ->
            Pid = spawn_link(fun() -> connection_worker(NextConnId) end),
            gen_server:reply(From, {ok, NextConnId}),
            FinalActive = maps:put(NextConnId, Pid, NewActive),
            {noreply, State#state{
                active_connections = FinalActive,
                waiting_queue = NewQueue
            }};

        {empty, _} ->
            {noreply, State#state{active_connections = NewActive}}
    end.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

%% Internal Functions
connection_worker(ConnId) ->
    timer:sleep(1000),  %% Simulate connection
    io:format("Connected ~s~n", [binary_to_list(ConnId)]),
    receive
        disconnect -> ok
    end.

%% Usage example
%% 1> {ok, Pool} = connection_pool:start_link(3).
%% 2> [connection_pool:connect(Pool, list_to_binary("conn-" ++ integer_to_list(I))) || I <- lists:seq(1, 5)].

Translation Strategy:

  1. Event loop → GenServer: Central coordinator manages connection state
  2. Semaphore → Capacity check: Manual tracking of active connections vs max
  3. Coroutines → Processes: Each connection is a separate process (true isolation)
  4. Async tasks → spawn_link: Lightweight processes with supervision
  5. Queue for backpressure: Explicitly manage waiting clients when pool full

Edge Cases & Gotchas:

  • Blocking vs message passing: Python blocks on semaphore; Erlang queues requests
  • Error handling: Python exceptions in event loop; Erlang links and monitors for crash detection
  • Process cleanup: Must handle process termination and queue cleanup
  • Timeout handling: Add receive timeout patterns for connection timeouts

Performance Implications:

  • Python asyncio: Single thread, cooperative scheduling, GIL limitations
  • Erlang: True concurrency, processes scheduled across cores, no GIL
  • Erlang's preemptive scheduling handles blocking better (processes don't block each other)
  • Consider supervision tree for fault tolerance in Erlang

Cross-reference: See convert-python-erlang for complete asyncio → BEAM translation patterns.


Supervision and Fault Tolerance Translation

Supervision is a key pattern in fault-tolerant systems. Different concurrency models provide different supervision mechanisms:

Source Model Target Model Translation Pattern
BEAM Supervision Trees Go error handling Explicit error returns, retry loops, health checks
BEAM Supervision Trees Rust Result + retry crate Result types for errors, tokio::task for spawning, manual restart
BEAM let-it-crash Clojure agents + error handlers Agent error-handler and error-mode, restart-agent
Go defer/panic/recover BEAM try/catch + supervisor Convert panic → exit, recover → supervisor restart
Python try/except BEAM let-it-crash Remove defensive error handling, use supervisor for recovery
Rust panic=abort BEAM supervision Change panic strategy to supervised process restart

Supervision Pattern: Erlang Supervision Tree → Go Error Handling

Erlang (Source - Let It Crash):

-module(worker_sup).
-behaviour(supervisor).

-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    SupFlags = #{
        strategy => one_for_one,      %% Restart individual workers
        intensity => 5,                %% Max 5 restarts
        period => 60                   %% In 60 seconds
    },

    ChildSpecs = [
        #{
            id => worker1,
            start => {worker, start_link, [worker1]},
            restart => permanent,      %% Always restart
            shutdown => 5000,
            type => worker
        }
    ],

    {ok, {SupFlags, ChildSpecs}}.

%% Worker module (crashes are OK, supervisor restarts)
-module(worker).
-behaviour(gen_server).

handle_call({process, Data}, _From, State) ->
    Result = risky_operation(Data),  %% May crash - that's fine!
    {reply, Result, State}.

risky_operation(Data) ->
    case Data of
        invalid -> error(badarg);     %% Crash - supervisor will restart
        _ -> process_data(Data)
    end.

Go (Target - Explicit Error Handling + Retry):

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "time"
)

// WorkerSupervisor manages worker lifecycle with restart logic
type WorkerSupervisor struct {
    maxRestarts int
    period      time.Duration
    restartLog  []time.Time
    workerFunc  func(context.Context) error
    ctx         context.Context
    cancel      context.CancelFunc
}

func NewWorkerSupervisor(
    maxRestarts int,
    period time.Duration,
    workerFunc func(context.Context) error,
) *WorkerSupervisor {
    ctx, cancel := context.WithCancel(context.Background())
    return &WorkerSupervisor{
        maxRestarts: maxRestarts,
        period:      period,
        workerFunc:  workerFunc,
        restartLog:  make([]time.Time, 0),
        ctx:         ctx,
        cancel:      cancel,
    }
}

func (s *WorkerSupervisor) Start() {
    go s.supervise()
}

func (s *WorkerSupervisor) Stop() {
    s.cancel()
}

func (s *WorkerSupervisor) supervise() {
    for {
        select {
        case <-s.ctx.Done():
            log.Println("Supervisor shutting down")
            return
        default:
            // Check restart intensity
            if s.exceedsRestartIntensity() {
                log.Println("Max restart intensity exceeded, shutting down")
                return
            }

            // Run worker
            err := s.workerFunc(s.ctx)
            if err != nil {
                log.Printf("Worker crashed: %v, restarting...", err)
                s.logRestart()
                time.Sleep(1 * time.Second) // Backoff before restart
            }
        }
    }
}

func (s *WorkerSupervisor) exceedsRestartIntensity() bool {
    now := time.Now()
    cutoff := now.Add(-s.period)

    // Remove old restart entries
    validRestarts := make([]time.Time, 0)
    for _, t := range s.restartLog {
        if t.After(cutoff) {
            validRestarts = append(validRestarts, t)
        }
    }
    s.restartLog = validRestarts

    return len(s.restartLog) >= s.maxRestarts
}

func (s *WorkerSupervisor) logRestart() {
    s.restartLog = append(s.restartLog, time.Now())
}

// Worker implementation
type Worker struct {
    id string
}

func (w *Worker) Run(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            // Simulate work with potential errors
            if err := w.riskyOperation(); err != nil {
                return fmt.Errorf("worker %s failed: %w", w.id, err)
            }
            time.Sleep(1 * time.Second)
        }
    }
}

func (w *Worker) riskyOperation() error {
    // Simulate occasional failures
    if time.Now().Unix()%10 == 0 {
        return errors.New("random failure")
    }
    log.Printf("Worker %s processing...", w.id)
    return nil
}

func main() {
    worker := &Worker{id: "worker1"}

    supervisor := NewWorkerSupervisor(
        5,                    // Max 5 restarts
        60*time.Second,       // In 60 seconds
        worker.Run,
    )

    supervisor.Start()

    // Run for demo duration
    time.Sleep(120 * time.Second)
    supervisor.Stop()
}

Translation Strategy:

  1. Supervisor behavior → Supervisor struct: Encapsulate restart logic in struct
  2. Restart strategies: Implement one-for-one manually (each worker independent)
  3. Intensity tracking: Log restart times, check against intensity limits
  4. Context for cancellation: Use context.Context for graceful shutdown
  5. Error propagation: Workers return errors instead of crashing

Supervision Translation Rules:

  • one_for_one → Independent goroutines with individual supervisors
  • one_for_all → Cancel all workers if one fails, restart all
  • rest_for_one → Cancel dependent workers (those started after failed worker)
  • Restart intensity → Track restart timestamps, check against limits

Error Isolation Strategies:

Language Isolation Mechanism Fault Containment
Erlang/Elixir Separate processes, supervision trees Process crash doesn't affect others
Go Goroutines + defer/recover, error returns Panic in goroutine kills goroutine only (with recover)
Rust Result/Option types, panic=abort or unwind Thread panic isolated, Result forces error handling
Clojure Agents with error-mode, futures with deref Agent errors caught in error-handler
Haskell Async with catches, STM for atomicity Exceptions in async isolated, catchable
Python Threading/asyncio with try/except Thread exceptions isolated, asyncio tasks can be cancelled

Cross-reference:

  • See convert-python-erlang for asyncio → supervision patterns
  • See convert-golang-rust for panic/recover → Result type patterns
  • See convert-clojure-elixir for agent error handling → OTP patterns

Common Concurrency Anti-Patterns to Avoid

When translating concurrency patterns, avoid these common mistakes:

  1. Literal Translation of Synchronization Primitives

    • ❌ Don't translate lock() in Python to Mutex in every target language
    • ✓ Use target's idiomatic concurrency: actors for Erlang, STM for Clojure, channels for Go
  2. Ignoring Backpressure

    • ❌ Don't translate bounded channels to unbounded Promises (memory leak!)
    • ✓ Implement explicit backpressure with async generators or custom queues
  3. Mixing Concurrency Models

    • ❌ Don't mix threads + async/await without understanding implications
    • ✓ Choose one concurrency model and use it consistently
  4. Over-Serialization

    • ❌ Don't funnel all parallel operations through a single actor/GenServer
    • ✓ Shard state across multiple actors/processes for scalability
  5. Under-Isolation

    • ❌ Don't share mutable state across goroutines without synchronization
    • ✓ Use message passing or proper synchronization primitives
  6. Ignoring Ordering Guarantees

    • ❌ Don't assume message ordering when target language doesn't guarantee it
    • ✓ Add explicit sequence numbers or use ordered channels if needed

Skill Template

When creating a new convert-X-Y skill, use this template:

---
name: convert-<source>-<target>
description: Convert <Source> code to <Target>. Use when migrating <Source> projects to <Target>, translating <Source> patterns to idiomatic <Target>, or refactoring <Source> codebases into <Target>. Extends meta-convert-dev with <Source>-to-<Target> specific patterns.
---

# Convert <Source> to <Target>

Convert <Source> code to idiomatic <Target>. This skill extends `meta-convert-dev` with <Source>-to-<Target> specific type mappings, idiom translations, and tooling.

## This Skill Extends

- `meta-convert-dev` - Skill creation patterns
- `meta-convert-guide` - Conversion methodology (APTV workflow, testing strategies)

## This Skill Adds

- **Type mappings**: <Source> types → <Target> types
- **Idiom translations**: <Source> patterns → idiomatic <Target>
- **Error handling**: <Source> exceptions/errors → <Target> approach
- **Async patterns**: <Source> async → <Target> async
- **Tooling**: <Source>-to-<Target> specific tools

## Quick Reference

| <Source> | <Target> | Notes |
|----------|----------|-------|
| `<type1>` | `<type1>` | ... |
| `<type2>` | `<type2>` | ... |

## [Continue with 8 Pillar sections...]

References

Reference Materials (in references/ directory)

Conversion Methodology

  • meta-convert-guide - APTV workflow, type system strategies, idiom patterns, testing approaches, common pitfalls

Skills That Extend This Meta-Skill

These skills provide concrete, language-pair-specific implementations:

  • convert-typescript-rust - TypeScript → Rust conversion
  • convert-typescript-python - TypeScript → Python conversion
  • convert-typescript-golang - TypeScript → Go conversion
  • convert-golang-rust - Go → Rust conversion
  • convert-python-rust - Python → Rust conversion

Related Meta-Skills

  • meta-library-dev - Library development patterns

Language Skills

For language-specific fundamentals (not conversion):

  • lang-typescript-dev - TypeScript development patterns
  • lang-python-dev - Python development patterns
  • lang-golang-dev - Go development patterns
  • lang-rust-dev - Rust development patterns

Commands

  • /create-lang-conversion-skill <source> <target> - Create a new conversion skill using this meta-skill as foundation