π Assignments: Phase 2 (Backend + first AI features)
W5 - Control Plane Skeleton + Mini DI Container
π― A5 orbit-api skeleton (Orbit v1 - Core Platform)
- Gradle multi-module; Spring Modulith modules
tenancy,workflows,prompts,tools,runswith module boundary tests - Workflow definitions: create a draft β validate against JSON Schema β publish immutable versions
- Flyway, ProblemDetail errors, OpenAPI, Testcontainers, ArchUnit (the domain must not depend on Spring/JPA)
- Acceptance:
./gradlew checkgreen; publishing the same version twice β 409
π¬ Lab (π΄): Mini DI container in Java: classpath scan for @Component, constructor injection, singleton scope, cycle detection with a readable error, @PostConstruct, and a JDK dynamic proxy for a @Timed annotation. Then explain how Spring does each of these (Spring Internals)
π§ Cognitive: step through refresh() in the debugger; draw the sequence from memory the next day
π Open: Workflow definitions as JSON documents (jsonb) vs normalized tables vs βworkflow as codeβ. Write ADR-004
Score: __/28
W6 - Data Layer + LLM Gateway v0 + First AI Use Case
π― A6
- JPA: optimistic locking on workflow publish; fix a deliberate N+1 (workflow β steps) 3 ways; keyset pagination on runs
- orbit-llm-gateway v0 (Go):
POST /v1/chatfor 2 providers (one hosted + Ollama locally) behind aProviderinterface (Strategy + Adapter); SSE streaming passthrough; timeouts; retries on 429/5xx with backoff; token usage in the response - UC1 v0: classify a support ticket β structured JSON (category, priority, entities) validated against a schema; on invalid output, re-ask with the validation error (β€ 2 attempts)
- Acceptance: β₯ 85% accuracy on a 50-ticket labeled set you create; the gateway adds < 10 ms p99 overhead (measure against a mock provider)
π¬ Lab: 5M rows in runs; optimize 5 queries (composite, partial, covering indexes) with EXPLAIN (ANALYZE, BUFFERS) before and after (PostgreSQL Internals)
π§ Cognitive: Predict β Verify: predict the chosen plan and row estimates before each EXPLAIN
π Open: Where do LLM request/response payloads (up to 200 KB) live? Postgres jsonb vs S3 (claim check) vs ClickHouse. Include cost math
Score: __/28
W7 - Multi-Tenancy & Security
π― A7
- Keycloak (org per tenant), JWT resource server, roles OWNER/BUILDER/VIEWER, method security
- API keys: generate
orb_live_<prefix>_<secret>, store a hash, look up by prefix, constant-time compare, rotate/revoke - Postgres Row-Level Security keyed on
app.tenant_idset per transaction; prove that forgetting aWHEREcanβt leak data - Idempotency-Key starter (a custom Spring Boot starter, Redis-backed): same key + same body β the same response; same key + a different body β 422
- Acceptance: an automated cross-tenant attack suite (20 cases: IDOR/BOLA, key reuse, JWT tampering) β all blocked
π¬ Lab: PortSwigger: access control + JWT labs (β₯ 6) π§ Cognitive: draw OAuth2 Auth Code + PKCE from memory, then check it against the RFC; list what each parameter defends against π Open: RLS vs app-level filters vs schema-per-tenant vs DB-per-tenant for 10 β 10,000 tenants Score: __/28
W8 - Tool Calling, gRPC & Quotas
π― A8
- orbit-worker (Go) with the tool-calling loop from scratch: model β
tool_useβ validate args against JSON Schema β execute βtool_resultβ model; max iterations, per-tool timeout, tool-output truncation, parallel tool calls - Built-in tools:
http_request(with an allowlist!),sql_readonly(read-only role +statement_timeout+ row limit),calculator,github_create_issue - UC1 v1: triage β if bug β create a GitHub issue via the tool β return a link
- gRPC contracts in
proto/with buf (lint + breaking checks); a Java client β Go server - Gateway quotas: token bucket per tenant in Redis Lua (RPM + TPM); 429 +
Retry-After - Acceptance: 200 concurrent goroutines never over-admit (verified by counting); the tool loop terminates on a looping model (use a mock that always calls a tool)
π¬ Lab: mini Redis: add replication or RDB persistence; Wireshark a gRPC call (Network Stack Internals) π§ Cognitive: Pre-mortem: 10 ways the tool-calling loop can fail in production (injection via tool output, a huge result, a hung tool, a non-idempotent retryβ¦) π Open: Design the tool-definition schema + versioning. How do you evolve a tool without breaking published workflows? Score: __/28
W9 - Streaming, GraphQL, UI & the v1 Ship
π― A9
- orbit-stream (Go): SSE endpoint fanning out tokens + run events from Redis pub/sub;
Last-Event-IDresume (keep the last N events per run in a Redis stream) - GraphQL (Spring for GraphQL): workflows β versions β runs β steps with
@BatchMapping - UC2 v0: chat playground with streaming + conversation memory (summarize after 10 turns)
- orbit-web: playground, run timeline, JSON workflow editor;
docker compose upbrings up everything - GitHub Actions: Java + Go matrices, cached builds < 6 min
β« Phase boss fight: load test v1 with 200 concurrent streaming chats + 50 runs/s against a mock provider (realistic TTFT/ITL distributions). Find and fix 2 bottlenecks using JFR/async-profiler + pprof. Publish the blog post βBuilding an LLM gateway in Goβ π§ Cognitive: Self-review: reread your W5 ADRs as a critical reviewer; write 3 critiques each Score: __/28