Xyberos — architecture baseline and roadmap¶
Python · Starlette · SQLite · Composable runtime · Explicit typed dependencies · Explicit Workflow · Experimental durability
This document is the stable architecture baseline: decisions, subsystem contracts, target structure, roadmap, and risk analysis. It is not the task tracker.
| Document | Owns |
|---|---|
PLAN.md (this file) |
Architecture baseline, subsystem contracts, roadmap rationale, risks, definition of done |
implementation.md |
Authoritative phase-by-phase work packages, exit gates, status, and cross-phase controls |
runtime.md |
The implemented Phase 1 public runtime surface |
tutorial.md |
The step-by-step tutorial, whose every output block is checked by a test |
architecture.md |
The reviewer's entry point: shape, mechanisms, trade-offs, gaps |
positioning.md |
Who it is for, who it is not for, and which default each seam replaces |
stability.md |
The frozen public surface: what is promised, in which tier, and how it changes |
release.md |
The release procedure, the artefact checks, and the dependency audit |
adr/ |
Immutable decisions that affect public behavior |
1. Final architecture decisions¶
The following decisions are now the foundation of the Xyberos implementation plan.
HTTP foundation: Starlette
Use Starlette for ASGI routing, middleware, request/response handling, and lifespan integration. Xyberos supplies the developer-facing application API above it.
Runtime: Composable Runtime + Capability Registry
Build a small application runtime with explicit service registration, dependency resolution, configuration, lifecycle, and plugin contracts. Do not introduce a large centralized kernel.
Dependency injection: Explicit typed dependencies
Use explicit dependency declarations and type annotations. Avoid hidden global service locators and fragile parameter-name-based injection.
Database: SQLite by default
Provide a zero-configuration SQLite setup and a database-provider contract. Reuse existing database libraries rather than building a custom ORM.
Execution: Direct execution + Workflow
Support ordinary function calls first, then explicit multi-step Workflow for branching, retries, timeouts, and cancellation.
Durability: Native SQLite MVP, experimental
Design the execution-store interface and prototype a single-instance durable executor. Do not make crash recovery a stable platform guarantee in the initial release.
Initial release scope¶
Stable target: Runtime + capability registry + Starlette integration + SQLite provider + plugins + explicit Workflow.
Experimental target: Native SQLite durable execution.
This distinction is important: the initial stable release must be useful even if the durable execution subsystem is disabled entirely.
2. Architecture overview¶
Developer API
App · Routes · Typed dependencies · Services · AI facade
Composable Runtime
Lifecycle · Configuration · Dependency resolution · Error handling
Capability Registry
Database · Auth · Storage · Cache · AI providers · Plugins
Execution Layer
Direct calls · Workflow · Experimental durable execution
HTTP adapter
Starlette · ASGI · Middleware
Persistence adapters
SQLite · Execution journal · Provider interfaces
The registry and execution layer are separate. The registry describes available capabilities; the execution layer determines how work runs. HTTP middleware is confined to HTTP concerns, and the durable executor does not become a mandatory path for ordinary application requests.
3. Core architectural contracts¶
Before implementing features, establish the contracts between subsystems. These contracts will determine whether Xyberos remains composable as it grows.
3.1 Application runtime¶
The runtime owns:
- Application configuration and environment settings.
- Component registration and dependency resolution.
- Startup, readiness, shutdown, and resource cleanup.
- Plugin initialization and validation.
- Logging integration and execution context.
- Application-wide error handling.
The runtime does not own database query logic, AI reasoning, HTTP middleware execution, or workflow history.
3.2 Capability registry¶
Each capability has a name, contract, provider, configuration, and lifecycle.
Example:
| Capability | Contract | Initial provider |
|---|---|---|
database |
Database provider | SQLite |
http |
HTTP adapter | Starlette |
cache |
Cache interface | In-memory |
storage |
Storage interface | Local filesystem |
ai |
Model provider interface | Optional provider |
knowledge |
Retrieval interface | Optional subsystem |
execution |
Execution interface | In-process |
durable |
Durable execution interface | Experimental SQLite |
Registry rules:
- Register capabilities before serving requests.
- Validate required dependencies during startup.
- Reject duplicate registrations by default.
- Support explicit provider replacement for testing and configuration.
- Define ownership and cleanup for every managed resource.
- Avoid a mutable global registry shared across unrelated application instances.
Use application-scoped dependencies for shared services, request-scoped dependencies for request-specific state, and transient dependencies only when a new instance is actually required.
3.3 Explicit typed dependency injection¶
Use a documented dependency marker or provider declaration. Do not infer dependencies merely from parameter names.
A proposed API:
from typing import Annotated
from xyberos import App, Inject, Scope, Settings
class UserService:
async def list_users(self) -> list[str]:
return []
app = App(settings=Settings.load())
app.provide(UserService, factory=UserService, scope=Scope.APP)
Phase 2 reuses the same marker for handler injection:
@app.get("/users")
async def list_users(users: Annotated[UserService, Inject()]) -> list[str]:
return await users.list_users()
The runtime should resolve the UserService annotation, construct or retrieve the appropriate instance, and fail at startup if a required dependency cannot be resolved.
The exact syntax may change during implementation, but the following behavior should not:
- Dependencies are declared explicitly.
- Type hints are checked.
- Dependency cycles produce actionable errors.
- Scope violations are rejected.
- Tests can override dependencies.
- Cleanup occurs in reverse dependency order.
3.4 Plugin contract¶
A plugin contributes capabilities and optionally routes, configuration, and lifecycle hooks.
class DatabasePlugin:
name = "database"
def register(self, registry, config):
registry.provide(
DatabaseProvider,
create_sqlite_provider(config.database),
scope="app",
)
This is illustrative pseudocode. The implementation should formalize the plugin interface and decide how configuration schemas, dependency declarations, and async lifecycle hooks are represented.
Keep plugins in-process for the first release. Treat third-party plugins as trusted code unless a separate isolation mechanism is implemented.
3.5 Provider interfaces¶
Use Python Protocol or abstract base classes for the boundaries that genuinely need substitution.
For example:
from typing import Any, Protocol
class ExecutionStore(Protocol):
async def create_execution(
self, execution_id: str, definition: str, input: Any
) -> None: ...
async def get_execution(self, execution_id: str) -> Any: ...
async def save_step_result(
self, execution_id: str, step_id: str, result: Any
) -> None: ...
The durable store will need additional operations for atomic state transitions, claims, retries, cancellation, and recovery. The initial contract should be finalized from those requirements rather than implemented as a generic key-value store.
4. Subsystem implementation plan¶
4.1 Runtime and application API¶
Build the runtime as the smallest usable unit.
Implementation tasks
- Create the
Appobject. - Add configuration loading and validation.
- Implement application and request contexts.
- Add typed dependency registration and resolution.
- Define startup and shutdown ordering.
- Add structured exceptions and logging integration.
- Validate registrations before the server accepts traffic.
Acceptance criteria
- A minimal app can start and shut down cleanly.
- Startup failure identifies the component and dependency involved.
- Resources are released even when another shutdown hook fails.
- Two app instances can run independently in the same process.
- The runtime does not require importing AI or durable execution packages.
4.2 Starlette integration¶
Starlette remains the HTTP engine. Xyberos should wrap it, not fork or reimplement it.
Implementation tasks
- Route registration through the Xyberos application API.
- Request and response handling.
- Typed dependency injection into handlers.
- Middleware configuration and ordering.
- Exception handling and validation integration.
- Lifespan coordination with the runtime.
- A test client and route-testing utilities.
- OpenAPI support through an appropriate integration.
Acceptance criteria
- Middleware behaves according to a documented ordering model.
- Runtime startup completes before requests are accepted.
- Shutdown waits for managed resources to close within configured limits.
- HTTP concerns do not leak into database, Workflow, or durable execution abstractions.
4.3 SQLite and database providers¶
SQLite should require no separate server setup for the initial application experience.
Implementation tasks
- Define the database provider interface.
- Integrate a mature SQLite driver and database-access library.
- Support configuration, connections, transactions, and cleanup.
- Integrate migrations instead of building a migration engine.
- Add test fixtures and temporary database support.
- Document concurrency limitations and deployment assumptions.
Acceptance criteria
- A fresh app can initialize its database with minimal configuration.
- Transaction failures roll back correctly.
- Connections and transactions have well-defined ownership.
- Tests cover concurrent requests and SQLite write contention.
- A future provider can be introduced without rewriting business services.
4.4 Workflow engine¶
Workflow is the explicit execution layer for multi-step work. It should not be required for ordinary functions or HTTP handlers.
Initial features
- Named steps with declared inputs and outputs.
- Sequential execution.
- Conditional branches.
- Bounded retries and backoff.
- Step and overall timeouts.
- Cancellation.
- Error propagation and handling policies.
- Execution metadata and tracing.
Defer initially
- Arbitrary distributed DAG scheduling.
- Automatic multi-agent planning.
- Unbounded loops.
- Implicit agent-generated control flow.
- Distributed task queues.
- Full replay and crash-recovery guarantees.
Acceptance criteria
- A workflow can be inspected and tested without a live model provider.
- Step failures produce structured execution errors.
- Retry counts and timeouts are deterministic and testable.
- Cancellation propagates to supported operations.
- No documentation implies persistence or recovery for ordinary in-process Workflow.
Shipped in Phase 4. All five criteria are now verified against the implementation: Workflow
lives in src/xyberos/execution/ with no optional dependency, the exit-gate suite in
tests/workflow/ covers every criterion deterministically with a virtual clock, and
docs/workflow.md states the absence of persistence where a reader would look for it. The frozen
contract is ADR 0009, and the naming decision is
ADR 0008.
4.5 AI facade¶
The AI facade should use the same capability and dependency model as other services.
Initial interfaces:
- Chat or text generation.
- Structured outputs.
- Model and provider configuration.
- Tool registration and invocation.
- Embeddings.
- Knowledge retrieval integration.
- Timeouts, token usage, and error reporting.
Model providers should be optional adapters. Keep provider-specific request formats outside the runtime core.
AI calls should use direct execution by default. A developer should opt into Workflow for explicit multi-step orchestration and into durable execution only when the task needs persisted progress and recovery.
Shipped in Phase 5. The facade, provider-neutral contracts, tool contract, closed schema subset,
and fake provider are dependency-free and live in src/xyberos/ai/, so "no AI SDK installed" is a
supported configuration rather than a degraded one; the Ollama adapter is the only module behind the
ollama extra. Chat, structured output, streaming, embeddings, and tool invocation all shipped, and
examples/ai_assistant runs them through the same DI, plugin, and Workflow contracts as any other
service. The frozen contract is ADR 0010 and the guide is
docs/ai.md.
Two interfaces in the list above did not ship, and the phase plan is why:
- Knowledge retrieval integration is deferred until it has its own storage, chunking, filtering, authorization, and evaluation contracts. Embeddings and the tool allowlist are the interface a retrieval layer would build on; the layer itself would be a guess today.
- Provider capability discovery is declared, not probed, with one exception: the Ollama
adapter cross-checks a declared
toolsclaim against what a model reports through/api/show, because that is the only capability the API exposes. The rest stay the operator's claim, which keeps a wrong claim a configuration bug instead of a silent false promise.
4.6 Experimental native SQLite durability¶
Implement this after the runtime and Workflow APIs are stable.
The initial durable executor should support:
- Persistent execution IDs.
- A defined state machine.
- Atomic execution and step-state updates.
- A persisted execution journal.
- Worker claims and leases.
- Bounded retries and retry scheduling.
- Cancellation requests.
- Recovery after process restart.
- Execution inspection.
- Versioned workflow definitions.
Use a separate execution-store interface. Do not put workflow history into the application database provider's general-purpose API.
Initial limitation: single-instance execution only. Do not claim safe multi-host execution or Temporal-equivalent guarantees.
Shipped in Phase 6. The executor lives in src/xyberos/durable/ behind the durable extra:
models.py holds the statuses and the transition table, journal.py owns a versioned SQLite schema
in its own file, store.py does transactional submission, atomic fenced claims, guarded writes, and
inspection, scheduler.py re-derives the next step from persisted state alone, worker.py runs one
step per claim with a lease heartbeat, recovery.py decides what an interrupted attempt means, and
definitions.py refuses to resume against a definition that no longer matches. The contract is
ADR 0011, the operator guide is docs/durable.md,
and the failure table in section 5.3 above is verified by a process-kill harness that kills a real
subprocess at each documented boundary.
Two properties are worth reading before the design: state is recovered per step, never per
bytecode, because workflow code is never persisted; and an attempt whose outcome cannot be
established is recorded as outcome_unknown and parked, never retried on a guess.
5. Native durable execution: detailed design¶
This subsystem is the highest-risk part of the architecture. Its design should be narrow enough to implement, but its persistence semantics must be correct.
5.1 Execution lifecycle¶
Non-terminal states:
PENDING— the execution is accepted and persisted but not yet owned.RUNNING— a worker owns an active lease.RETRYING— a failed attempt is being rescheduled.WAITING— the execution is parked on a scheduled retry or an external signal.
Terminal states: COMPLETED, FAILED, CANCELLED, and TIMED_OUT. Legal transitions must be validated and persisted atomically.
A production implementation needs a formal transition table, including how cancellation interacts with a running step and how expired leases are recovered.
5.2 Initial persistence schema¶
Use SQLite tables with transactional updates. A starting schema can contain:
| Table | Purpose |
|---|---|
workflow_definitions |
Workflow name, version, and definition metadata |
executions |
Execution ID, input, status, timestamps, and workflow version |
execution_steps |
Step ID, status, attempts, result reference, and timestamps |
execution_events |
Ordered execution state transitions and diagnostic metadata |
worker_leases |
Ownership, lease expiry, and fencing information |
scheduled_retries |
Retry eligibility and next-run time |
Keep the execution store distinct from application business tables. Store large step outputs separately when necessary, and avoid storing credentials or unrestricted sensitive payloads in execution history.
SQLite transactions should protect related state changes. A worker should claim work atomically, and expired leases should only permit recovery after the previous owner is no longer authorized to commit state.
5.3 Recovery rules¶
The durable executor must define what happens at each failure boundary.
| Failure | Required behavior |
|---|---|
| Crash before a step starts | Recover and schedule the step |
| Crash after a result is persisted | Reuse the recorded result |
| Crash during an activity | Retry or reconcile according to its policy |
| Lost external API response | Use idempotency or reconciliation; outcome may be uncertain |
| Expired worker lease | Recover only after validating lease ownership and fencing |
| Invalid workflow version | Stop safely and report a compatibility error |
| Cancellation during execution | Record cancellation and stop supported work safely |
Important: a persisted step result is not proof that an external side effect happened exactly once. If an email, payment, or API call succeeds and the process crashes before saving the result, the executor needs idempotency support or a reconciliation strategy.
5.4 Why durability remains experimental¶
A single SQLite database can be sufficient for an early local execution engine, but durability includes more than writing rows.
Before promotion to stable status, demonstrate:
- Atomic claims and state transitions.
- Safe restart recovery.
- Idempotent result persistence.
- Lease expiration and ownership fencing.
- Workflow definition compatibility.
- Retry and cancellation correctness.
- Bounded resource consumption.
- Reliable database backup and recovery procedures.
Do not add distributed scheduling or multi-host guarantees until the single-instance model is reliable.
6. Target repository structure¶
Use a modular monolith, with optional subsystems separated by import and dependency boundaries.
The tree below is the target layout. It is not a description of the current tree, and it is not a requirement to create empty modules: implement a module only when its responsibilities and contracts are understood. Phases 1–6 currently implement:
src/xyberos/
├── __init__.py # public re-exports
├── _extras.py # missing-extra guidance for optional imports
├── app.py # App, RequestContext, lifecycle, plugins
├── config.py # typed settings, including plugin and ai sections
├── deprecation.py # the deprecation warning and decorator
├── diagnostics.py # provider and plugin diagnostics
├── errors.py # error taxonomy, including the workflow and AI families
├── routing.py # dependency-free route records
├── runtime/
│ ├── __init__.py
│ ├── context.py # contextvars and logging helpers
│ ├── dependencies.py # registry, records, graph validation
│ └── lifecycle.py # LifecycleState
├── plugins/ # trusted plugin contract, configuration, and registry
│ ├── protocol.py, config.py, registry.py
├── execution/ # in-process workflow engine (core, no extra)
│ ├── models.py # immutable Workflow, Step, Edge, results
│ ├── policies.py # retry, backoff, clock, cancellation, step context
│ ├── validation.py # build-time graph validation
│ ├── executor.py # structured-concurrency execution
│ ├── events.py # structured execution events
│ ├── direct.py # the single-call execution boundary
│ └── testing.py # ManualClock, FakeExternalService, Reply
├── ai/ # provider-neutral facade (core, no dependency)
│ ├── contracts.py # requests, responses, usage, capabilities, protocols
│ ├── schema.py # closed JSON Schema subset
│ ├── tools.py # allowlisted tool contract and audit events
│ ├── facade.py # AIFacade and the logging policy
│ ├── testing.py # FakeModelProvider and scripted replies
│ └── ollama.py # the Ollama adapter; only module behind the "ollama" extra
├── durable/ # experimental durable executor (optional "durable" extra)
│ ├── contracts.py # wall clock protocol and system clock
│ ├── models.py # statuses, the transition table, and inspection records
│ ├── journal.py # versioned journal schema and settings
│ ├── store.py # submission, atomic claims, guarded writes, inspection
│ ├── definitions.py # definition lookup and fingerprint matching
│ ├── scheduler.py # the next action, derived from persisted state alone
│ ├── recovery.py # what an interrupted attempt means
│ ├── worker.py # claim, run one step, commit, yield
│ └── testing.py # ManualWallClock and deterministic lease expiry
├── http/ # optional "http" extra (Starlette)
│ ├── adapter.py, middleware.py, routing.py, requests.py, testing.py
└── database/ # optional "database" extra (SQLAlchemy Core, aiosqlite, Alembic)
├── contracts.py, migrations.py, sqlite.py, testing.py
examples/
├── hello_app/ # the smallest complete application, walked through in getting-started
├── tutorial/ # the tutorial's runnable chapters, one module each
├── tenancy/ # a verified principal as an explicit argument, with tests
├── broker_worker/ # a queue owns delivery, the journal owns progress, with tests
├── sqlite_crud/ # worked CRUD slice with Alembic migrations
├── custom_plugin/ # plugin authoring example
├── workflow/ # workflow example with retries, branching, and events
├── ai_assistant/ # AI facade wired through a plugin, DI, routes, and a workflow
└── durable_workflow/ # durable example: a worker dies between steps, and mid-step
tests/
├── runtime/ # Phase 1 exit-criteria tests
├── http/ # adapter and handler-compilation tests
├── database/ # provider, PRAGMA, and contention tests
├── plugins/ # plugin contract, configuration, and lifecycle tests
├── workflow/ # Phase 4 exit-criteria tests
├── ai/ # Phase 5 exit-criteria tests, including the Ollama adapter
├── durable/ # Phase 6 exit-criteria tests for the journal, store, and worker
├── crash_recovery/ # Phase 6 process-kill harness and its shared definition
├── integration/ # end-to-end example, migration, plugin-package, workflow, and AI tests
├── fixtures/ # separately packaged plugin used as a test fixture
├── contracts/ # executable public-API examples
├── test_import_boundary.py
├── test_public_api.py
├── test_public_surface.py # the frozen per-module surface
├── test_compatibility.py # pinned configuration, plugin, and workflow metadata
├── test_deprecation.py
├── test_optional_dependencies.py # a missing extra names the extra to install
└── test_package.py # the built artefact, checked from outside
The target layout is:
xyberos/
├── pyproject.toml
├── README.md
├── CHANGELOG.md # Keep a Changelog; Unreleased, then one section per release
├── SECURITY.md # vulnerability response policy
├── docs/
│ ├── PLAN.md # architecture baseline (this file)
│ ├── implementation.md # phase plan and status
│ ├── getting-started.md # from an empty directory to a running application
│ ├── tutorial.md # the step-by-step tutorial, checked output and all
│ ├── architecture.md # the reviewer's guide to the design
│ ├── stability.md # what is promised, and how it changes
│ ├── runtime.md # implemented runtime surface
│ ├── http.md # HTTP adapter guide
│ ├── database.md # database provider guide
│ ├── plugins.md # plugin guide
│ ├── workflow.md # workflow engine guide
│ ├── ai.md # AI facade guide
│ ├── durable.md # durable executor operator guide
│ ├── testing.md # deterministic doubles and test habits
│ ├── troubleshooting.md # error to cause to fix
│ ├── security.md # trust model, secrets, and limits
│ ├── cli.md # the four commands
│ ├── release.md # release procedure, artefact checks, dependency audit
│ ├── performance.md # the recorded resource baselines and what they mean
│ ├── positioning.md # who it is for, and which seam replaces each default
│ ├── database-providers.md # writing a provider for another database
│ └── adr/ # decision records
├── examples/
│ ├── hello_app/
│ ├── sqlite_crud/
│ ├── custom_plugin/
│ ├── workflow/
│ ├── ai_assistant/
│ └── durable_workflow/
├── src/
│ └── xyberos/
│ ├── __init__.py
│ ├── app.py
│ ├── config.py
│ ├── errors.py
│ ├── runtime/
│ │ ├── lifecycle.py
│ │ ├── context.py
│ │ └── dependencies.py
│ ├── plugins/
│ │ ├── base.py
│ │ └── loader.py
│ ├── http/
│ │ ├── adapter.py
│ │ ├── routing.py
│ │ └── middleware.py
│ ├── database/
│ │ ├── contracts.py
│ │ └── sqlite.py
│ ├── execution/
│ │ ├── direct.py
│ │ ├── events.py
│ │ ├── executor.py
│ │ ├── models.py
│ │ ├── policies.py
│ │ ├── testing.py
│ │ └── validation.py
│ ├── ai/
│ │ ├── facade.py
│ │ ├── providers.py
│ │ └── tools.py
│ ├── durable/
│ │ ├── contracts.py
│ │ ├── models.py
│ │ ├── store.py
│ │ ├── journal.py
│ │ ├── scheduler.py
│ │ ├── worker.py
│ │ └── recovery.py
│ └── cli/ # implemented (Phase 7): check, new, run, migrate
└── tests/
├── test_import_boundary.py # implemented
├── test_public_api.py # implemented
├── test_package.py # implemented
├── runtime/ # implemented (Phase 1)
├── http/ # implemented (Phase 2)
├── database/ # implemented (Phase 2)
├── plugins/ # implemented (Phase 3)
├── workflow/ # implemented (Phase 4)
├── ai/ # implemented (Phase 5)
├── durable/ # implemented (Phase 6)
├── crash_recovery/ # implemented (Phase 6, process-kill harness)
├── cli/ # implemented (Phase 7)
├── perf/ # implemented (Phase 7, resource baselines)
├── tutorial/ # implemented (Phase 7, the tutorial's chapter tests)
├── fixtures/ # implemented (separately packaged plugin)
├── contracts/ # implemented (executable API examples)
├── integration/ # implemented
The directory layout is a target, not a requirement to create empty modules upfront. Implement a module only when its responsibilities and contracts are understood. A target module may be split once its real responsibilities are clear, as execution/ and durable/ both were: execution's seven modules and durable's nine are separate because they are separately testable — the scheduler is a pure function, the store is the only writer, recovery is the only place that decides what an interrupted attempt means — not because the plan predicted those exact names.
Keep durable/ importable independently of the core. An installation that does not use experimental durability does not need its dependencies, and its journal is created only when something opens it.
7. Development roadmap¶
The roadmap prioritizes a usable stable runtime before experimental durability. Work packages,
exit gates, and current status live in implementation.md; this section
is the summary.
| Phase | Scope | Deliverable | Estimate | Status |
|---|---|---|---|---|
| 0 | Architecture and API specification | ADRs and executable API-contract examples | 3–5 days | complete |
| 1 | Runtime foundation | A working minimal application | 1–2 weeks | complete |
| 2 | HTTP and traditional application support | A working CRUD application | 1–2 weeks | complete |
| 3 | Plugins and capabilities | An independently developed plugin | 1–2 weeks | complete |
| 4 | Stable in-process Workflow | A tested, inspectable workflow engine without durability promises | 2–3 weeks | complete |
| 5 | AI facade and integration | An AI application built on the same runtime and Workflow APIs | 1–2 weeks | complete |
| 6 | Experimental durable execution | An experimental single-instance executor with documented limits | 3–6+ weeks | complete |
| 7 | DX, hardening, and release | A stable runtime and Workflow release; durability remains experimental | 1–2 weeks | complete |
| 8 | Boundary legibility and extension points | Recorded refusals, a reusable provider contract, tested integration recipes, and capacity guidance — with core unchanged | 3–5 days | complete |
Estimated initial implementation: approximately 10–18+ working weeks for one experienced developer, including a limited experimental durable executor. The work can take longer if building and validating recovery semantics reveals additional requirements.
The stable runtime and Workflow can be released earlier; there is no reason to block the useful framework on durable execution.
8. Testing and quality gates¶
Testing should be built into each phase, not postponed until the end.
| Area | Required tests |
|---|---|
| Runtime | Lifecycle ordering, configuration validation, isolated app instances |
| Dependency injection | Missing providers, cycles, scope validation, overrides |
| Plugins | Dependency order, duplicate registration, startup failure, cleanup |
| HTTP | Routing, middleware order, exception handling, async behavior |
| SQLite | Transactions, migrations, connection cleanup, concurrent writes |
| Workflow | Branches, retry limits, timeouts, cancellation, error propagation |
| AI | Provider failures, malformed outputs, tool permissions, usage limits |
| Durability | Crash recovery, duplicate work, lease expiry, interrupted steps |
| Packaging | Clean installation, optional dependencies, supported Python versions |
Published identity¶
This line publishes as xyberos 2.0.0. The xyberos name is already held on the package index by
the published 1.x line, which installs the same import root and cannot be co-installed, so 0.1.0
would be unreachable through a plain install and would read as a regression. The leading digit records
lineage, not maturity — the 3.0 used as a working name for this rewrite is not a published version
number — and the maturity claim is carried by the Development Status classifier and by the tiers in
stability.md. The decision and its cost are in
ADR 0017.
Required release gates¶
Stable runtime gate
A developer can build, test, and run a SQLite-backed HTTP application without importing AI or durability modules.
Stable Workflow gate
Execution order, branching, retries, cancellation, and errors have documented semantics and automated tests.
Experimental durability gate
Automated process-kill and restart tests validate the documented recovery behavior. Unsupported guarantees are explicitly excluded.
Use CI to run unit tests, integration tests, type checking, linting, package builds, and dependency-compatibility checks. Add deterministic fault-injection tests for the durable executor.
9. Developer experience requirements¶
Xyberos should feel like a normal Python framework rather than an infrastructure platform.
Prioritize these deliverables:
- A minimal application template.
- A SQLite CRUD example.
- A plugin-development example.
- An AI assistant example.
- A Workflow example with branching and retries.
- A separate experimental durable-workflow example.
- Clear errors when configuration or dependencies are invalid.
- A test client and easy dependency overrides.
- Documentation that distinguishes direct, in-process, and durable execution.
The command-line interface can initially be limited to project creation, development server startup, and configuration or diagnostics commands. Do not build a large CLI before the underlying APIs are stable.
Shipped in Phase 7, with that limit kept. The command has four subcommands — check, new,
run, migrate — and each is a wrapper over an API the runtime already proves: check is
Settings.load plus App.validate() plus App.diagnostics(), run hands an import string to
whatever ASGI server is installed rather than bundling one, and migrate calls the Alembic runner
the database extra exposes. It is argparse from the standard library, so a framework that refuses
to depend on an HTTP stack does not depend on a CLI framework either.
10. Risks and mitigations¶
| Risk | Mitigation |
|---|---|
| Runtime becomes a new kernel | Keep core responsibilities limited to lifecycle, composition, and dependency resolution |
| Registry becomes a global service locator | Use explicit typed injection and scoped dependencies |
| Workflow grows into a universal workflow framework | Limit stable Workflow to a well-defined set of execution primitives |
| SQLite executor makes unsafe recovery claims | Keep it experimental and single-instance until failure semantics are validated |
| Plugin API becomes unstable | Finalize lifecycle and registration contracts before broad plugin development |
| Too many abstractions slow development | Reuse existing Python libraries and introduce interfaces where substitution is valuable |
| AI functionality dominates the core | Keep AI providers and knowledge integrations optional |
| Async code blocks the server | Define policies for blocking I/O, CPU-intensive work, and worker execution |
11. Definition of done for Xyberos¶
The first stable release is ready when a developer can:
- Create a project with a single application object.
- Expose HTTP routes using Starlette underneath.
- Use SQLite without configuring an external database server.
- Inject services using explicit typed dependencies.
- Add and replace capabilities through plugins.
- Build ordinary applications without loading AI or Workflow.
- Build explicit, testable multi-step workflows.
- Add an AI provider and tools without changing the runtime architecture.
- Test applications without requiring live infrastructure wherever practical.
- Understand exactly which execution modes offer persistence and recovery.
Durable execution remains experimental until its persistence, recovery, and side-effect guarantees are independently demonstrated.
Final architecture decision record¶
Foundation
Composable runtime + capability registry + explicit typed dependencies.
Application infrastructure
Starlette + SQLite default + modular plugin contracts.
Execution
Direct execution by default, explicit in-process Workflow for multi-step work, and optional experimental native SQLite durability.
Release priority
Ship a useful, developer-friendly runtime and Workflow first. Do not delay the stable platform to achieve production-grade durability in version 3.0.
The central architectural rule for Xyberos is:
Compose the application through the runtime, expose functionality through capabilities, define multi-step behavior through Workflow, and add durable execution only when recovery is required.
This provides a manageable implementation path while keeping the framework extensible enough for conventional applications, AI systems, and future reliable workflow execution.
Appendix A — Reviewed recommendations and disposition¶
An external review proposed five changes to this baseline. Each proposal was evaluated against
the project constraints (thin runtime, no optional imports in core, validated failure semantics)
and folded into the plan as follows. Accepted items are already reflected in sections 4 and 5
and in implementation.md.
| # | Recommendation | Disposition | Where it landed |
|---|---|---|---|
| 1 | Force WAL mode and a bounded busy timeout; keep the durable journal in a separate SQLite file | Accepted | Phase 2 work packages 5–6 and the durable requirements in section 5 |
| 2 | Bake contextvars into the runtime and copy context across step, task, and thread boundaries |
Accepted | src/xyberos/runtime/context.py (Phase 1); Phase 4 propagates it across steps |
| 3 | Validate the dependency graph at startup instead of per request | Accepted | Phase 1 validates before readiness; see ADR 0005 |
| 4 | Add a decorator or imperative Workflow API alongside the declarative builder | Deferred | Deferred until declarative Workflow introspection and recovery semantics are proven; see the backlog in implementation.md |
| 5 | Move the AI facade ahead of the stable Workflow engine | Rejected | Workflow semantics stabilize first; Phase 4 uses a deterministic fake provider, so no reordering is needed |
Rationale for the two proposals that were not accepted:
- Decorator Workflow (deferred). An imperative
async def workflow(ctx)body is arbitrary Python control flow. It cannot be reliably inspected, retried, or recovered, so it cannot carry the stable Workflow contract. A decorator surface can be added later as sugar over the declarative graph, once the graph semantics are frozen. - AI facade before Workflow (rejected). AI adapters are the largest source of optional dependencies and provider churn. Gating core Workflow work on an adapter would invert the dependency direction and slow the stable release. Phase 4 instead exercises retries, deadlines, and cancellation against a deterministic in-process fake that needs no network or model provider.
Housekeeping in this revision: the collapsed code blocks in sections 3.3–3.5 were repaired, the scrambled execution-state list in section 5.1 was rewritten, the repository tree in section 6 now distinguishes the implemented Phase 1 layout from the target layout, and the pasted review text that previously ended this file was replaced by this appendix. The file is UTF-8 without a BOM.