Repository map and agent tooling¶
Where things live, what looks wrong but is intentional, and the agent tools
wired into the repository. This page is for framework contributors; a module
author needs only the public sandloom.sdk API
(SDK reference).
Repository map¶
src/sandloom/
sdk/ # Public API 1.0: the ONLY framework code a business module and
# its tests import (plus generated contract stubs and DTOs).
# Public modules: module, app, migrations, tooling, web, data,
# tenant, events, effects, contracts, authz, sagas, projections,
# infra, security, errors, testing. Each declares __all__ and
# __beginner__; the root sandloom.sdk re-exports the beginner names.
sdk/_decl/, sdk/_prim/ # the declarative layer (typed events, @contract,
# define_module discovery) and the primitives behind it;
# private (leading underscore), never imported by modules
core/ # framework kernel — touch only for framework work. Never
# imported by a business module (`sandloom check` SANDLOOM-C002).
# Internals are grouped into thematic subpackages:
core/observability/ # logging, metrics, tracing, guardrails (redaction)
core/eventing/ # event_bus, transport, core_events (channel schemas)
core/http/ # middlewares, error_handlers, rate_limit
core/migrations/ # migration runner (`sandloom migrate`), alembic_support
core/loader/ # plugin_loader, service_registry (module wiring)
core/app_factory.py # kernel create_app() (public as sandloom.sdk.app.create_app);
# sandloom/main.py only adds every installed sandloom.modules
# entry point (the kernel names no module); enforce_configuration:
# the boot-time config checks every entry point runs (app,
# worker, `sandloom migrate`, the sandloom CLI)
core/composition.py # SANDLOOM_COMPOSITION_ROOT resolution for worker/migrate/CLIs
core/config/ # configuration layer (imports nothing above it):
# settings (Settings, one section per domain), tiers,
# accepted_risks
core/auth/ # hashing, tokens (JWT), bearer, the subject-state
# record (marks), writers / verification, blocks,
# recovery
core/cli/ # `sandloom new`, `sandloom add` (generators and templates),
# `sandloom check`, `sandloom test`, the ops CLIs, gunicorn_conf
core/archcheck/ # architecture analyzers behind `sandloom check`
core/testing/ # the test harness behind sandloom.sdk.testing (pytest
# plugin, fixtures, UoW test helpers)
core/sagas/ # saga runtime (ADR-0008): registry checks, step
# protocol, outcome table, timer sweep, operator ops
core/worker.py # Celery worker/beat entrypoint
core/infra/ # infrastructure plugin layer: capability contracts, the
# InfraRegistry, built-in drivers, config — see
# Capabilities & Infrastructure
shared/ # cross-module DTOs and generated contract stubs
identity/ # users, JWT auth, RBAC, 2FA, SSO
tenancy/ # organizations, membership (owner/admin/member), invitations
billing/ # plans, subscriptions, credits ledger, Stripe
tests/architecture/_fixtures/ # deliberately-broken example modules
# (.py.txt data, NOT importable, never shipped) the
# analyzer self-tests scan for positive detection
migrations/core/versions/ # CORE chain: shared infra tables only
# (outbox/audit/consumer_inbox/contract_command_log),
# applied to every hosting database; shipped in the
# wheel as sandloom/core/migrations/core_chain. Each
# module owns its OWN chain in
# src/sandloom/<module>/migrations/
architecture_rules.yml # import-boundary manifest (main.py is the
# platform composition root and is allowlisted
# there; the wiring itself lives in
# core/app_factory.py inside the kernel)
public_api_baseline.json # every public sandloom.sdk name and signature
# (a change fails `sandloom test`, SANDLOOM-T008)
deploy/ # monolith + microservices compose profiles
# (deploy/README.md is the deployment reference)
docs/ # docs site sources; archive/ = legacy and Russian
# specs; tutorial-app/ = the generated application
# the tutorial and how-tos include code from
admin/ # platform-owner console (React + Vite)
See Capabilities & Infrastructure for the plugin layer and Versioning & Stability for what is public.
Things that look wrong but are intentional¶
- Architecture analyzer fixtures are deliberately-broken example modules stored
as
.py.txtdata undertests/architecture/_fixtures/(NOT importable, never shipped in the wheel). The self-tests read them by path and run each analyzer to prove it still CATCHES its violation (positive detection). See that directory'sREADME.md. ORM_LEAKAGE_BASELINEintests/architecture/test_api_no_orm_leakage.py— legacy identity debt ratchet; entries may be removed, never added.FakeStripeClient— the default PSP (BILLING_PROVIDER=fake) outside prod; it always rejects unsigned webhooks (tests sign withsign_stripe_payload). Production usesBILLING_PROVIDER=stripe+ keys./health/simpleis process liveness./healthand/readyreturn 503 when degraded — the readiness contract, not a bug. Public callers see check statuses only; check details and/ready's module/consumer topology requireX-Internal-Token. Results are cached forHEALTH_CACHE_TTL_SECONDS.- App-wide error handling lives in the kernel (
core/http/error_handlers.py; the framework owns it, modules never register their own). - Duplicate event deliveries can happen (post-commit dispatch + relay race) — by design, at-least-once; the stable envelope id makes them deduplicable.
Agent tooling (MCP servers and skills)¶
The repository registers no MCP servers; agents use their standard file and
search tools plus the project's skills and the sandloom CLI.
- Skills — reusable workflows under
.agents/skills/and.claude/skills/(projectsandloom-*skills plus the mattpocock engineering/productivity/misc set, e.g.tdd,grill-me,diagnosing-bugs,handoff). sandloom docs <name>prints a public name's exact signature, tier and docstring;sandloom explain <code>explains an error code. Both answer without reading kernel files.
MCP servers are registered for Copilot (.vscode/mcp.json) and Claude Code
(.mcp.json); Codex is wired globally via codex mcp add / ~/.codex.
Inspect before changing a module¶
Use module context to gather scoped rules, SDK and wire interfaces, and the declared architecture graph to inspect contract/event connections. Both commands can be invoked by an agent and read their index without booting the app.