Skip to main content

CHANGELOG.md

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.7] - 2026-06-26

### Added
- **Injector strict_mode support**: `OrchidSymbiont.Hooks.Injector` now reads `:strict_mode` from the workflow baggage. When `strict_mode: true`, the resolver will not fall back to the global catalog, ensuring strict multi-tenant isolation at the hook level.

  ```elixir
  Orchid.run(recipe, params,
    baggage: %{scope_id: "tenant-1", strict_mode: true}
  )
  ```

## [0.2.5] & [0.2.6] - 2026-04-24

### Enhenced
- **Documents**: Updated documents.

## [0.2.4] - 2026-03-26

### Enhenced
- **Symbiont ID Type**: Now Symbiont's ID also support binary. *If the name is dynamically generated in your application, it is recommended to use binary instead of atom to avoid memory leaks.*

### Changed
- **Scopa Name Declaration**: Now we use `:scope_id` instead `:session_id` to seperate domains.

### Migration Guide (0.2.3 -> 0.2.4)

```elixir
# Before
{OrchidSymbiont.Runtime, session_id: "my_session"}
OrchidSymbiont.register("my_session", :worker, {MyWorker, []})
Orchid.WorkflowCtx.put_baggage(:session_id, "my_session")
# After
{OrchidSymbiont.Runtime, scope_id: "my_session"}
OrchidSymbiont.register("my_session", :worker, {MyWorker, []})
Orchid.WorkflowCtx.put_baggage(:scope_id, "my_session")
```

## [0.2.3] - 2026-03-25

### Fix

- Fixed a bug where adding options other than session_id would prevent the creation of additional processes.

## [0.2.2] - 2026-03-25

### Added

- **Strict Mode**: A new `strict_mode: true` option has been added to `OrchidSymbiont.Resolver.resolve/3`. When enabled, if the corresponding service blueprint is not found in the current Session's Catalog, an error will be thrown directly, and **there will be no fallback** to the global Catalog, thus ensuring stricter multi-tenant security isolation.

- **State Backup and Restore**: The `dump/1` and `restore/2` methods have been added to `OrchidSymbiont.Catalog` for easy exporting and restoring of the current Catalog's state.

### Changed

- **[Architecture Refactoring] True Session Isolation**: The Session's supervised tree model has been refactored. Now, when you start a Session using `OrchidSymbiont.Runtime`, it launches a dedicated `OrchidSymbiont.Catalog` process for that Session. Registration states between different Sessions are now truly physically isolated, instead of being distinguished by tuples `{{session_id, name}, val}` in the global state.

- **[Performance Optimization] Catalog Underlying Replacement**: The underlying implementation of `OrchidSymbiont.Catalog` has been simplified from `GenServer` to `Agent`, making it more lightweight and better suited to its pure state storage nature.

- ****Startup Layer Optimization**: The application startup process has been streamlined. The bloated list of child processes in OrchidSymbiont.Application has been removed. The startup logic for global services (`Registry`, global `Catalog`, `DynamicSupervisor`, `Preloader`) is now consolidated and wrapped within the `:global` initialization strategy of `OrchidSymbiont.Runtime`.

### Removed

- Removed `clear/0` and `clear_session/1` in `OrchidSymbiont.Catalog` module. Since Sessions now manage their lifecycle through an independent supervision tree, their internal Catalog processes are automatically destroyed and reclaimed when the corresponding `Runtime` terminates, eliminating the need for manual cleanup.

## [0.2.1] - 2026-03-24

### BREAKING CHANGE

- Move legacy module name `Orchid.Symbiont` into `OrchidSymbiont`.

### Fixed
- **Atom Exhaustion Vulnerability**: Resolved a critical memory leak issue where dynamic session IDs (atoms) were never garbage collected, potentially causing VM crashes in long-running multi-tenant applications.

### Changed
- **Session ID Type**: Changed `session_id` from `atom()` to `binary()` (String). Example: `:project_a` → `"project_a"`.
- **Unified Registry Architecture**: Replaced per-session Registry/Catalog/Preloader processes with a single global Registry using compound keys `{session_id, name}`. This significantly reduces process overhead for high-session-count scenarios.
- **Runtime Implementation**: `Orchid.Symbiont.Runtime` now uses `DynamicSupervisor` directly instead of wrapping it in a `Supervisor`.
- **Simplified Naming Module**: Refactored `Orchid.Symbiont.Naming` with cleaner API:
  - `get_registry/0` - Returns the unified registry
  - `session_supervisor/1` - Returns via tuple for session supervisor
  - `worker/2` - Returns via tuple for session workers

### Removed
- Per-session Registry processes (now uses global with compound keys)
- Per-session Catalog processes (now uses single global instance)
- Per-session Preloader processes (now uses global `Orchid.Symbiont.Preloader`)
- `Naming.registry/1`, `Naming.catalog/1`, `Naming.preloader/1` functions

### Migration Guide (0.2.0 → 0.2.1)

```elixir
# Before (0.2.0)
{Orchid.Symbiont.Runtime, session_id: :my_session}
Orchid.Symbiont.register(:my_session, :worker, {MyWorker, []})
Orchid.WorkflowCtx.put_baggage(:session_id, :my_session)

# After (0.2.1)
{OrchidSymbiont.Runtime, session_id: "my_session"}
OrchidSymbiont.register("my_session", :worker, {MyWorker, []})
Orchid.WorkflowCtx.put_baggage(:session_id, "my_session")
```

## [0.2.0] - 2026-01-04

### Added
- **Session Isolation (Multi-Tenancy)**: Introduced the ability to run completely isolated sandboxes of Symbiont processes using `session_id`. Perfect for concurrent workflows or multi-tenant SaaS architectures without PID conflicts.
- **Dynamic Supervision Trees**: `Orchid.Symbiont.Runtime` can now be started multiple times under different namespaces by passing `[session_id: :your_session_name]`. Each session gets its own isolated `Registry`, `DynamicSupervisor`, `Catalog`, and `Preloader`.
- **Smart Catalog Fallback**: Added a hierarchical lookup mechanism in `Orchid.Symbiont.Catalog`. If a blueprint is not found in a session-specific catalog, it will automatically fall back to the global catalog. *(Write blueprints once globally, run instances locally!)*
- **New Naming Module**: Added `Orchid.Symbiont.Naming` to handle dynamic process registration routing transparently.

### Changed
- **Injector Hook Upgraded**: `Orchid.Symbiont.Hooks.Injector` now automatically extracts `:session_id` from the `Orchid.WorkflowCtx` baggage and routes the process resolution to the corresponding session's registry.
- **API Enhancements**: `Orchid.Symbiont.register` and `Orchid.Symbiont.Resolver.resolve` now accept an optional `session_id` parameter. *(Fully backward compatible with global singleton usage)*.
- **Dependencies Bump**: Updated `orchid` to `0.5.6`, `telemetry` to `1.4.1`, and `ex_doc` to `0.40.1`.

## [0.1.4] - 2026-01-03

### Added
- **Global Mapping Support**: `Orchid.Symbiont.Hooks.Injector` now supports resolving symbiont aliases from the `Orchid.WorkflowCtx` baggage. 
- **Hierarchical Resolution**: Symbiont mappings are now merged from two levels:
    1. **Global**: Set via `Orchid.WorkflowCtx.put_baggage(ctx, :symbiont_mapper, [...])`.
    2. **Local**: Set via `step_opts`. 
    *Note: Local step-specific mappings will override global mappings if both define the same logical name.*

### Fixed
- Internal `get_headers` logic in the Injector hook to properly handle the workflow context.

## [0.1.3] - 2026-01-02

### Changed
- **Breaking**: Renamed `Orchid.Symbiont.Step.get_required/0` with old name called `get_step_required_mapper/0` for shorter and cleaner API usage.
- `Orchid.Symbiont.call/3` now uses `apply/3` for dynamic module invocation.

### Added
- `Orchid.Symbiont.preload/1` now accepts a single symbiont name in addition to a list of names.

## [0.1.2] - 2026-01-02

### Dependencies

- update orchid's version to `0.5`

## [0.1.1] - 2025-12-27

### Dependencies

- use a looser version constraint for better compatibility with future patch releases

## [0.1.0] - 2025-12-26

### Added

- **Initial Release**: Launched `OrchidSymbiont` as the official process management extension for the [Orchid](https://hex.pm/packages/orchid) workflow engine.
- **Lazy Loading**: Introduced a mechanism to start GenServers (Symbionts) only when requested by a workflow step, optimizing resource usage for heavy tasks (e.g., ML models, DB connections).
- **Dependency Injection**: Added `Orchid.Symbiont.Hooks.Injector` to automatically resolve and inject running process references (PIDs) into Steps implementing the `Orchid.Symbiont.Step` behavior.
- **Lifecycle Management (TTL)**:
  - Implemented an `Idle Shutdown` mechanism using a transparent Wrapper/Proxy pattern.
  - Workers configured with a `:ttl` option will automatically terminate after a period of inactivity to free up resources.
  - The Wrapper ensures request forwarding is transparent and handles timeouts (`:infinity` internally) correctly to support long-running tasks.
- **Registry & Catalog**:
  - `Orchid.Symbiont.register/2`: API to define blueprints for services, supporting both standard startup and TTL-enabled modes.
  - Built-in `Registry` for name resolution and idempotency check (preventing duplicate startups).
- **Integration**:
  - Provides `Orchid.Symbiont.Step` behavior requiring `required/0` (dependencies) and `run_with_model/3` (execution logic).
  - Added helper `Orchid.Symbiont.call/3` for ergonomic synchronous communication with Symbionts.

### Dependencies

- Requires **Orchid ~> 0.4.0** (utilizes the new Context and Hook architecture).