This commit is contained in:
valenti
2026-09-08 07:21:34 -04:00
commit a4b224da8e
169 changed files with 27483 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
# ADR-0001 — Monorepo Structure for ASF
- **Status:** Accepted
- **Date:** 2026-09-07
- **Deciders:** ASF Architecture Team
## Context
Azurian Software Factory comprises several logical components:
a shared domain model, an event schema package, connectors, multiple backend services
(core, context, ai, worker) and a Next.js web frontend.
We must choose a repository organization that balances:
- Strong cohesion for shared types / contracts
- Independent deployability of services
- Local developer ergonomics
- Clear service boundaries
## Decision
Use a **single monorepo** with the following top-level layout:
```
ASF/
├── frontend/asf-web/ # Next.js SaaS UI
├── services/ # Deployable Python services
│ ├── asf-core/
│ ├── asf-context/
│ ├── asf-ai/
│ └── asf-worker/
├── packages/ # Shared installable Python packages
│ ├── asf-domain/
│ ├── asf-events/
│ └── asf-connectors/
├── docs/ # Architecture, ADRs, API docs
├── infrastructure/ # Dockerfiles, scripts
└── tests/ # Cross-service integration tests
```
Shared packages are installed in editable mode (`pip install -e`) when
developing locally.
## Consequences
### Positive
- Shared domain models and event schemas live in one place — no drift.
- Refactors that cross service boundaries are done atomically in one PR.
- Local `docker-compose` up brings up the whole stack trivially.
- Search / navigation works across the whole product.
### Negative
- CI pipelines must be careful to only rebuild what changed.
- Service release tagging must be explicit (single git log, many artefacts).
### Risks / Mitigations
- Accidental tight coupling between services → mitigated by code review,
AGENT.md rules, and keeping services import only from `packages/*`, never
from each other.
@@ -0,0 +1,63 @@
# ADR-0002 — MongoDB as Primary Persistence
- **Status:** Accepted
- **Date:** 2026-09-07
- **Deciders:** ASF Architecture Team
## Context
ASF requires a primary store for Organizations, Projects, Artifacts,
ArtifactRelationships, Jobs, AuditEvents and similar tenant-scoped records.
Key properties required:
- Flexible schema: the generic `Artifact` model needs extensible `metadata`.
- Document-level locality: an Artifact document bundles relationships list,
metadata and version.
- Horizontal scaling and multi-tenancy (every query is scoped by `tenant_id`).
- Async/await Python driver support.
## Decision
Adopt **MongoDB 7.x** as the only primary operational database.
Use compound indexes prefixed with `tenant_id` for all tenant-owned
collections. Collections are intentionally few:
- `organizations`
- `users`
- `teams`
- `projects`
- `artifacts`
- `artifact_relationships`
- `integrations`
- `jobs`
- `ai_executions`
- `audit_events`
**We do NOT** create a collection per artifact type. Every artifact type
(requirement, task, commit, test, deployment…) lives in the single
`artifacts` collection discriminated by `type`.
MinIO holds large binaries / reports; MongoDB stores the object reference
and metadata.
## Consequences
### Positive
- Flexible `metadata` on Artifacts matches the "Everything is an Artifact"
principle without schema migrations for every new artifact subtype.
- Compound `(tenant_id, project_id, …)` indexes give fast tenant-scoped
reads and guarantee isolation at query time.
- Native async driver (`motor`) integrates naturally with FastAPI.
### Negative
- No foreign-key constraints across collections — referential integrity is
an application responsibility (mitigated via service-layer validations
and tests).
- Relationship graph traversal requires multiple queries or the
`$graphLookup` aggregation stage. The schema is intentionally designed
so that a future graph-database projection is possible without rewriting
domain logic.
### Follow-up
- ADR for graph-database introduction once traversal patterns stabilize.
- ADR for vector / embedding store (Semantic Context Engine phase).
@@ -0,0 +1,61 @@
# ADR-0003 — RabbitMQ as Event and Job Backbone
- **Status:** Accepted
- **Date:** 2026-09-07
- **Deciders:** ASF Architecture Team
## Context
ASF has two asynchronous messaging needs:
1. **Domain events** (facts that happened — `artifact.created`,
`deployment.completed`, …) used for decoupled side effects.
2. **Asynchronous jobs** (AI inference, indexing, code analysis, testing)
that need separate queues with priorities and horizontal workers.
A single backbone that handles both patterns is preferred to reduce
infrastructure surface.
## Decision
Adopt **RabbitMQ 3.13+** as the sole event/job backbone.
### Logical queues
| Queue | Purpose | Priority |
|------------------------|----------------------------------------|----------|
| `asf.ai.interactive` | Latency-sensitive user-facing AI work | High |
| `asf.ai.background` | Batch / low-priority AI work | Normal |
| `asf.indexing` | Repository / document indexing | Low |
| `asf.code_analysis` | Static analysis tasks | Normal |
| `asf.testing` | Test execution requests | Normal |
| `asf.devops` | Deployment / CI orchestration | Normal |
| `asf.notifications` | Emails / webhooks / in-app notif. | Normal |
### Source of truth
RabbitMQ delivers work. **Persistent job state** (status, timestamps,
usage, error, correlation) lives in MongoDB `jobs` collection. Workers
update Mongo as they progress through `queued → running → completed|failed`.
RabbitMQ is **never** the permanent system of record for job state.
### Event format
Every event message contains:
```
event_id, event_type, timestamp, tenant_id, project_id?,
correlation_id, causation_id?, payload
```
Large payloads / binaries go to MinIO; the message carries only a
reference.
## Consequences
### Positive
- One broker, well-understood routing / priority / TTL / DLX semantics.
- Workers are stateless, horizontally scalable processes.
- At-least-once delivery matches domain; idempotency keys on workers
prevent duplicate side-effects.
### Negative
- Exactly-once is not provided — consumers must be idempotent.
- Operational complexity of a broker (mitigated by managed services in prod).
@@ -0,0 +1,65 @@
# ADR-0004 — AI Gateway Abstraction Layer
- **Status:** Accepted
- **Date:** 2026-09-07
- **Deciders:** ASF Architecture Team
## Context
ASF requires LLM capabilities for:
- Code analysis
- Requirement decomposition
- Test generation
- Context-aware chat ("Ask ASF")
- Incident analysis
Target inference backends include on-prem DGX Spark, OpenAI-compatible
local APIs, commercial providers, and customer-hosted endpoints.
Tenants may have different AI policies (allowed models, budgets,
geographic restrictions).
## Decision
Introduce an **ASF AI Gateway** service (`services/asf-ai/`) with a
stable Python interface.
### Interface
Business code requests **capabilities**, never models:
```python
class AIGateway(Protocol):
async def analyze_code(self, request: CodeAnalysisRequest) -> AnalysisResult: ...
async def generate_tests(self, request: TestGenerationRequest) -> GeneratedTests: ...
async def chat(self, request: ChatRequest, ctx: AIContext) -> ChatResponse: ...
async def decompose_requirement(self, req: RequirementDecomposeRequest) -> Decomposition: ...
```
Provider adapters implement the gateway. Model selection, routing,
priority queuing, usage metering, cost tracking and tenant AI policy
live inside the gateway — not in callers.
### Bootstrap
The first implementation ships with a `MockProvider` that returns
deterministic canned responses with realistic latencies. Real providers
(OpenAI-compatible, NVIDIA, etc.) are added as adapters later without
touching domain/business code.
## Consequences
### Positive
- No LLM provider lock-in — swap / add adapters freely.
- Usage and cost tracking centralized.
- Tenant-level policy enforceable at one boundary.
- Tests for business logic run fast against the mock — no external API
calls, no flakiness.
### Negative
- One more abstraction — the initial API surface must be designed
carefully so future providers truly fit.
### Mitigation
- Provider adapters are written early for at least two real providers,
to validate the abstraction before domain code depends heavily on it.