Repo Structure

The monorepo layout for core, apps, shared packages, and archive modules, with boundary rules.

This repo is a monorepo: core stays stable, apps iterate fast, and shared packages form the only legal bridge.

Top-level layout

syris/
├── core/
│   └── syris_core/                      # SYRIS core (Python, uv)
│       ├── pyproject.toml
│       ├── alembic.ini
│       ├── src/syris_core/              # import namespace
│       └── tests/
├── apps/
│   ├── dashboard/                  # mission control (Next.js)
│   ├── docs/                       # internal docs (Next.js + Fumadocs)
│   └── nutrition/                  # standalone app(s)
├── packages/
│   ├── shared-types/               # cross-app contracts (TS/JSON schema)
│   ├── api-client/                 # typed client for core API (TS)
│   └── ui/                         # optional shared UI primitives
├── archive/                        # deprecated modules (read-only)
└── ops/                            # deployment and ops artifacts

syris core internal layout

src/syris_core/
├── __init__.py
├── main.py                          # Boot: wire all components, start loops
├── config.py                        # Typed settings (Pydantic BaseSettings)
│
├── schemas/                         # Pure Pydantic v2 — no DB, no logic
│   ├── __init__.py
│   ├── common.py                    # Enums, ErrorDetail, shared value types
│   ├── events.py                    # MessageEvent, RoutingDecision, GateSpec
│   ├── tasks.py                     # Task, Step, RetryPolicy, StepOutcome
│   ├── tools.py                     # ToolCall, ToolResult, ToolCallContext,
│   │                                #   RegisteredTool, ToolHealth, TrustPolicy
│   ├── audit.py                     # AuditEvent
│   ├── approvals.py                 # Approval
│   ├── schedules.py                 # Schedule
│   ├── rules.py                     # Rule, Condition, Action types
│   └── health.py                    # SystemHealth, Alarm
│
├── storage/                         # DB layer (PostgreSQL)
│   ├── __init__.py
│   ├── db.py                        # Engine, session factory, connection config
│   ├── models.py                    # SQLAlchemy ORM models (all tables)
│   ├── repos/                       # One repo per aggregate root
│   │   ├── __init__.py
│   │   ├── events.py
│   │   ├── audit.py
│   │   ├── tasks.py
│   │   ├── tools.py                 # tool_calls, tool_results, idempotency_outcomes
│   │   ├── approvals.py
│   │   ├── schedules.py
│   │   ├── watchers.py
│   │   ├── rules.py
│   │   ├── health.py                # system_health, alarms
│   │   └── projections.py           # proj_* tables read/write
│   └── migrations/                  # Alembic
│       ├── env.py
│       └── versions/
│
├── pipeline/                        # The three-stage pipeline
│   ├── __init__.py
│   ├── runner.py                    # Main loop: normalise → route → execute
│   ├── normaliser.py                # Raw payload → MessageEvent + audit
│   ├── router.py                    # Orchestrates routing layers in order
│   └── executor.py                  # Dispatches to fast/task/gated/sandbox
│
├── routing/                         # Router internals (peer to pipeline/)
│   ├── __init__.py
│   ├── filters.py                   # Hard filters: dedup, spam, quiet hours
│   ├── fastpath.py                  # Deterministic intent matching
│   ├── rules_eval.py                # Condition evaluator for Rule objects
│   └── llm_fallback.py              # LLM-based routing (last resort)
│
├── tasks/                           # Task engine
│   ├── __init__.py
│   ├── engine.py                    # Claim → execute → checkpoint loop
│   ├── step_runner.py               # Execute one step, handle outcomes
│   ├── state.py                     # State machine enforcement
│   └── recovery.py                  # Startup reconciliation
│
├── tools/                           # Tool runtime
│   ├── __init__.py
│   ├── registry.py                  # ToolRegistry: register, lookup, health
│   ├── executor.py                  # Scope → risk → gate → idempotency → call
│   ├── idempotency.py               # Outcome store operations
│   ├── base.py                      # BaseTool ABC
│   └── builtin/
│       └── noop.py                  # NoopTool for testing
│
├── safety/                          # Safety layer
│   ├── __init__.py
│   ├── autonomy.py                  # Read/write current level + history
│   ├── risk.py                      # Risk classifier + adjusters
│   ├── gates.py                     # Gate matrix + override evaluation
│   └── dryrun.py                    # Preview protocol
│
├── scheduler/                       # Schedules only
│   ├── __init__.py
│   └── loop.py                      # Scheduler tick loop
│
├── watchers/                        # Watcher subsystem (top-level peer)
│   ├── __init__.py
│   ├── loop.py                      # Watcher tick loop
│   ├── base.py                      # BaseWatcher ABC
│   └── heartbeat.py                 # HeartbeatWatcher
│
├── rules/                           # Rules engine (top-level peer)
│   ├── __init__.py
│   ├── engine.py                    # Load rules, evaluate, dispatch actions
│   └── cache.py                     # In-memory cache with TTL + invalidation
│
├── mcp/                             # MCP integration (top-level peer)
│   ├── __init__.py
│   ├── connection.py                # MCPConnectionManager
│   ├── provider.py                  # MCPProvider: discovery → registry sync
│   ├── adapter.py                   # MCPToolAdapter(BaseTool)
│   └── trust.py                     # TrustPolicy schema + loader
│
├── adapters/                        # Inbound/outbound adapters
│   ├── __init__.py
│   ├── inbound/
│   │   ├── __init__.py
│   │   └── base.py                  # InboundAdapter ABC
│   └── outbound/
│       ├── __init__.py
│       └── base.py                  # OutboundAdapter ABC
│
├── observability/                   # Audit, projections, health
│   ├── __init__.py
│   ├── audit.py                     # AuditWriter — sole emit point
│   ├── projections.py               # Sync projection updaters
│   ├── health.py                    # SystemHealth writer
│   └── alarms.py                    # Alarm creation + dedup + lifecycle
│
├── secrets/
│   ├── __init__.py
│   └── store.py                     # SecretsStore protocol + Fernet impl
│
├── api/                             # FastAPI
│   ├── __init__.py
│   ├── app.py                       # FastAPI app factory
│   ├── deps.py                      # Dependency injection (session, services)
│   └── routes/
│       ├── __init__.py
│       ├── status.py                # /health, /state
│       ├── events.py                # /events
│       ├── audit.py                 # /audit, /artifacts/{id}
│       ├── tasks.py                 # /tasks CRUD + cancel/pause/resume
│       ├── approvals.py             # /approvals + approve/deny
│       ├── schedules.py             # /schedules CRUD
│       ├── watchers.py              # /watchers
│       ├── rules.py                 # /rules
│       ├── integrations.py          # /integrations
│       ├── controls.py              # /controls/pause, resume, autonomy
│       └── alarms.py                # /alarms + ack/resolve
│
└── workers/                         # Milestone 7 — skeleton only
    ├── __init__.py
    └── manager.py

schemas/ contains Pydantic v2 data contracts only — no DB access, no logic. ORM models live in storage/models.py. routing/ is separate from pipeline/ so that fastpath.py and rules_eval.py can be unit-tested without instantiating the full pipeline.

Boundary rules

Apps are sandboxed

Anything under apps/* is a standalone application and must not import from core/*.

Allowed: apps/*packages/*

Not allowed: apps/*core/*

Core owns integrations

All integration code lives under core/syris/ because integrations deploy together with the control plane, share the secrets and scopes policy, and participate in the same audit and trace runtime.

Storage layer access

storage/repos/ contains one repository object per aggregate root. Pipeline and task engine code imports from repos, not from the ORM directly. This keeps the DB layer swappable — change repo internals, callers do not change.

Running locally

Core:

uv sync
uv run python -m syris.main    # control plane + API server

Apps:

bun run dev            # in each app directory (dashboard / docs / nutrition)