@plazmodium/odin 0.3.2-beta → 0.3.4-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +82 -11
  2. package/builtin/ODIN.md +1045 -0
  3. package/builtin/agent-definitions/README.md +170 -0
  4. package/builtin/agent-definitions/_shared-context.md +377 -0
  5. package/builtin/agent-definitions/architect.md +627 -0
  6. package/builtin/agent-definitions/builder.md +716 -0
  7. package/builtin/agent-definitions/discovery.md +293 -0
  8. package/builtin/agent-definitions/documenter.md +238 -0
  9. package/builtin/agent-definitions/guardian.md +1049 -0
  10. package/builtin/agent-definitions/integrator.md +363 -0
  11. package/builtin/agent-definitions/planning.md +236 -0
  12. package/builtin/agent-definitions/product.md +405 -0
  13. package/builtin/agent-definitions/release.md +430 -0
  14. package/builtin/agent-definitions/reviewer.md +447 -0
  15. package/builtin/agent-definitions/watcher.md +402 -0
  16. package/builtin/skills/api/graphql/SKILL.md +548 -0
  17. package/builtin/skills/api/grpc/SKILL.md +554 -0
  18. package/builtin/skills/api/rest-api/SKILL.md +469 -0
  19. package/builtin/skills/api/trpc/SKILL.md +503 -0
  20. package/builtin/skills/architecture/clean-architecture/SKILL.md +141 -0
  21. package/builtin/skills/architecture/domain-driven-design/SKILL.md +129 -0
  22. package/builtin/skills/architecture/event-driven/SKILL.md +145 -0
  23. package/builtin/skills/architecture/microservices/SKILL.md +143 -0
  24. package/builtin/skills/architecture/tla-precheck/SKILL.md +171 -0
  25. package/builtin/skills/backend/golang-gin/SKILL.md +141 -0
  26. package/builtin/skills/backend/nodejs-express/SKILL.md +277 -0
  27. package/builtin/skills/backend/nodejs-fastify/SKILL.md +152 -0
  28. package/builtin/skills/backend/python-django/SKILL.md +128 -0
  29. package/builtin/skills/backend/python-fastapi/SKILL.md +140 -0
  30. package/builtin/skills/database/mongodb/SKILL.md +132 -0
  31. package/builtin/skills/database/postgresql/SKILL.md +120 -0
  32. package/builtin/skills/database/prisma-orm/SKILL.md +366 -0
  33. package/builtin/skills/database/redis/SKILL.md +140 -0
  34. package/builtin/skills/database/supabase/SKILL.md +416 -0
  35. package/builtin/skills/devops/aws/SKILL.md +382 -0
  36. package/builtin/skills/devops/docker/SKILL.md +359 -0
  37. package/builtin/skills/devops/github-actions/SKILL.md +435 -0
  38. package/builtin/skills/devops/kubernetes/SKILL.md +459 -0
  39. package/builtin/skills/devops/terraform/SKILL.md +453 -0
  40. package/builtin/skills/frontend/alpine-dev/SKILL.md +27 -0
  41. package/builtin/skills/frontend/angular-dev/SKILL.md +28 -0
  42. package/builtin/skills/frontend/astro-dev/SKILL.md +28 -0
  43. package/builtin/skills/frontend/htmx-dev/SKILL.md +28 -0
  44. package/builtin/skills/frontend/nextjs-dev/SKILL.md +470 -0
  45. package/builtin/skills/frontend/react-patterns/SKILL.md +166 -0
  46. package/builtin/skills/frontend/svelte-dev/SKILL.md +28 -0
  47. package/builtin/skills/frontend/tailwindcss/SKILL.md +131 -0
  48. package/builtin/skills/frontend/vuejs-dev/SKILL.md +28 -0
  49. package/builtin/skills/generic-dev/SKILL.md +307 -0
  50. package/builtin/skills/testing/cypress/SKILL.md +372 -0
  51. package/builtin/skills/testing/jest/SKILL.md +176 -0
  52. package/builtin/skills/testing/playwright/SKILL.md +341 -0
  53. package/builtin/skills/testing/unit-tests-eval-sdd/SKILL.md +73 -0
  54. package/builtin/skills/testing/unit-tests-sdd/SKILL.md +83 -0
  55. package/builtin/skills/testing/vitest/SKILL.md +249 -0
  56. package/dist/adapters/skills/filesystem.d.ts.map +1 -1
  57. package/dist/adapters/skills/filesystem.js +2 -18
  58. package/dist/adapters/skills/filesystem.js.map +1 -1
  59. package/dist/builtin-assets.d.ts +8 -0
  60. package/dist/builtin-assets.d.ts.map +1 -0
  61. package/dist/builtin-assets.js +90 -0
  62. package/dist/builtin-assets.js.map +1 -0
  63. package/dist/init.js +69 -11
  64. package/dist/init.js.map +1 -1
  65. package/dist/schemas.d.ts +1 -1
  66. package/dist/server.js +1 -1
  67. package/dist/server.js.map +1 -1
  68. package/dist/tools/prepare-phase-context.d.ts.map +1 -1
  69. package/dist/tools/prepare-phase-context.js +5 -0
  70. package/dist/tools/prepare-phase-context.js.map +1 -1
  71. package/dist/types.d.ts +3 -0
  72. package/dist/types.d.ts.map +1 -1
  73. package/package.json +5 -3
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: domain-driven-design
3
+ description: Domain-Driven Design patterns — bounded contexts, aggregates, and strategic design
4
+ category: architecture
5
+ version: "1.0"
6
+ compatible_with:
7
+ - clean-architecture
8
+ - event-driven
9
+ - microservices
10
+ ---
11
+
12
+ # Domain-Driven Design (DDD)
13
+
14
+ ## Overview
15
+
16
+ DDD is a software design approach that models complex business domains through a shared ubiquitous language, bounded contexts, and tactical patterns like aggregates and domain events.
17
+
18
+ ## Strategic Design
19
+
20
+ ### Bounded Contexts
21
+
22
+ ```
23
+ ┌─────────────────┐ ┌──────────────────┐ ┌───────────────┐
24
+ │ Identity & │ │ Ordering │ │ Shipping │
25
+ │ Access │ │ │ │ │
26
+ │ │ │ Order │ │ Shipment │
27
+ │ User │◄──►│ LineItem │◄──►│ Tracking │
28
+ │ Role │ │ Cart │ │ Address │
29
+ │ Permission │ │ Payment │ │ Carrier │
30
+ └─────────────────┘ └──────────────────┘ └───────────────┘
31
+ Context Map: Published Language / Anti-Corruption Layer
32
+ ```
33
+
34
+ Each bounded context has its own:
35
+ - **Ubiquitous language** — "User" in Identity vs "Customer" in Ordering
36
+ - **Models** — same real-world concept, different representations
37
+ - **Data store** — ideally its own database/schema
38
+
39
+ ### Context Mapping
40
+
41
+ | Pattern | When to Use |
42
+ |---------|------------|
43
+ | **Shared Kernel** | Two teams co-own a small shared model |
44
+ | **Customer-Supplier** | Upstream provides what downstream needs |
45
+ | **Anti-Corruption Layer** | Translate between contexts to prevent leaking |
46
+ | **Published Language** | Well-defined API/events for integration |
47
+
48
+ ## Tactical Patterns
49
+
50
+ ### Aggregate
51
+
52
+ ```typescript
53
+ // Aggregate Root — consistency boundary
54
+ class Order {
55
+ private items: OrderItem[] = [];
56
+ private status: OrderStatus = 'draft';
57
+
58
+ addItem(product: ProductRef, quantity: number, price: Money): void {
59
+ if (this.status !== 'draft') throw new DomainError('Cannot modify submitted order');
60
+ const existing = this.items.find(i => i.productId === product.id);
61
+ if (existing) {
62
+ existing.increaseQuantity(quantity);
63
+ } else {
64
+ this.items.push(new OrderItem(product.id, quantity, price));
65
+ }
66
+ }
67
+
68
+ submit(): DomainEvent[] {
69
+ if (this.items.length === 0) throw new DomainError('Cannot submit empty order');
70
+ this.status = 'submitted';
71
+ return [new OrderSubmitted(this.id, this.total(), this.items.length)];
72
+ }
73
+
74
+ get total(): Money {
75
+ return this.items.reduce((sum, item) => sum.add(item.subtotal()), Money.zero());
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### Domain Events
81
+
82
+ ```typescript
83
+ // Events represent something that happened in the domain
84
+ class OrderSubmitted implements DomainEvent {
85
+ readonly occurredAt = new Date();
86
+ constructor(
87
+ public readonly orderId: string,
88
+ public readonly total: Money,
89
+ public readonly itemCount: number,
90
+ ) {}
91
+ }
92
+
93
+ // Handle in application layer
94
+ class OnOrderSubmitted {
95
+ constructor(private emailService: EmailService, private inventoryService: InventoryService) {}
96
+
97
+ async handle(event: OrderSubmitted): Promise<void> {
98
+ await this.emailService.sendOrderConfirmation(event.orderId);
99
+ await this.inventoryService.reserveItems(event.orderId);
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Repository
105
+
106
+ ```typescript
107
+ // Repository per aggregate root — NOT per table
108
+ interface OrderRepository {
109
+ findById(id: OrderId): Promise<Order | null>;
110
+ save(order: Order): Promise<void>; // Saves entire aggregate
111
+ nextId(): OrderId;
112
+ }
113
+ ```
114
+
115
+ ## Best Practices
116
+
117
+ 1. **One aggregate = one transaction** — don't modify multiple aggregates in one transaction
118
+ 2. **Reference aggregates by ID** — not by direct object reference
119
+ 3. **Small aggregates** — only include what's needed for invariant enforcement
120
+ 4. **Domain events for cross-aggregate communication** — eventual consistency between aggregates
121
+ 5. **Ubiquitous language** — code names must match domain expert vocabulary
122
+ 6. **Anti-Corruption Layer** — always translate at context boundaries
123
+
124
+ ## Gotchas
125
+
126
+ - **Anemic domain model** — entities with only getters/setters and all logic in services
127
+ - **Big aggregate** — putting everything in one aggregate kills performance
128
+ - **Premature context splitting** — start with one context, split when language diverges
129
+ - **Ignoring the domain expert** — DDD requires ongoing collaboration, not just patterns
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: event-driven
3
+ description: Event-driven architecture patterns — event sourcing, CQRS, message brokers, and async workflows
4
+ category: architecture
5
+ version: "1.0"
6
+ compatible_with:
7
+ - domain-driven-design
8
+ - microservices
9
+ - redis
10
+ ---
11
+
12
+ # Event-Driven Architecture
13
+
14
+ ## Overview
15
+
16
+ Event-driven architecture (EDA) uses events as the primary mechanism for communication between components. Events represent facts — things that have happened — and enable loose coupling, scalability, and auditability.
17
+
18
+ ## Patterns
19
+
20
+ ### Event Notification
21
+
22
+ ```
23
+ Producer → Event Bus → Consumer(s)
24
+
25
+ OrderService NotificationService
26
+ │ │
27
+ ├── publishes ──► OrderPlaced ──► │ sends email
28
+ │ │
29
+ InventoryService PaymentService
30
+ │ │
31
+ ├── subscribes ─► OrderPlaced ──► │ charges card
32
+ ```
33
+
34
+ ### Event Sourcing
35
+
36
+ ```typescript
37
+ // Store events, not state — rebuild state by replaying events
38
+ class OrderEventStore {
39
+ async save(orderId: string, events: DomainEvent[]): Promise<void> {
40
+ for (const event of events) {
41
+ await this.db.insert('events', {
42
+ stream_id: orderId,
43
+ type: event.type,
44
+ data: JSON.stringify(event),
45
+ version: event.version,
46
+ occurred_at: event.occurredAt,
47
+ });
48
+ }
49
+ }
50
+
51
+ async load(orderId: string): Promise<DomainEvent[]> {
52
+ return this.db.query(
53
+ 'SELECT * FROM events WHERE stream_id = $1 ORDER BY version ASC',
54
+ [orderId]
55
+ );
56
+ }
57
+ }
58
+
59
+ // Rebuild aggregate from events
60
+ function rehydrate(events: DomainEvent[]): Order {
61
+ const order = new Order();
62
+ for (const event of events) {
63
+ order.apply(event); // Mutates internal state
64
+ }
65
+ return order;
66
+ }
67
+ ```
68
+
69
+ ### CQRS (Command Query Responsibility Segregation)
70
+
71
+ ```
72
+ Commands (Write) Queries (Read)
73
+ │ │
74
+ ▼ ▼
75
+ ┌──────────┐ events ┌──────────────────┐
76
+ │ Write DB │ ──────────► │ Read Projections │
77
+ │ (events) │ │ (denormalized) │
78
+ └──────────┘ └──────────────────┘
79
+ ```
80
+
81
+ ```typescript
82
+ // Command side — validates and stores events
83
+ async function placeOrder(cmd: PlaceOrderCommand): Promise<void> {
84
+ const order = await eventStore.load(cmd.orderId);
85
+ const events = order.place(cmd.items); // Returns new events
86
+ await eventStore.save(cmd.orderId, events);
87
+ await eventBus.publish(events);
88
+ }
89
+
90
+ // Query side — read from optimized projections
91
+ async function getOrderSummary(orderId: string): Promise<OrderSummary> {
92
+ return readDb.query('SELECT * FROM order_summaries WHERE id = $1', [orderId]);
93
+ }
94
+
95
+ // Projector — updates read model when events arrive
96
+ class OrderProjector {
97
+ async handle(event: OrderPlaced): Promise<void> {
98
+ await readDb.insert('order_summaries', {
99
+ id: event.orderId,
100
+ status: 'placed',
101
+ total: event.total,
102
+ item_count: event.items.length,
103
+ });
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Message Broker Patterns
109
+
110
+ ```typescript
111
+ // Dead letter queue for failed messages
112
+ const config = {
113
+ queue: 'order-processing',
114
+ deadLetterQueue: 'order-processing-dlq',
115
+ maxRetries: 3,
116
+ retryDelay: [1000, 5000, 30000], // Exponential backoff
117
+ };
118
+
119
+ // Idempotent consumers — handle duplicate delivery
120
+ async function handleOrderPlaced(event: OrderPlaced): Promise<void> {
121
+ const processed = await cache.get(`processed:${event.id}`);
122
+ if (processed) return; // Already handled
123
+
124
+ await processOrder(event);
125
+ await cache.set(`processed:${event.id}`, '1', 'EX', 86400);
126
+ }
127
+ ```
128
+
129
+ ## Best Practices
130
+
131
+ 1. **Events are facts** — immutable, past-tense (`OrderPlaced`, not `PlaceOrder`)
132
+ 2. **Idempotent consumers** — always handle duplicate delivery gracefully
133
+ 3. **Schema evolution** — version events; add fields, don't rename/remove
134
+ 4. **Dead letter queues** — capture failed messages for manual inspection
135
+ 5. **Correlation IDs** — trace events across services with a shared ID
136
+ 6. **Eventually consistent** — accept that read models may lag; design UX accordingly
137
+ 7. **Start without event sourcing** — event notification is simpler; add sourcing when you need audit trails
138
+
139
+ ## Gotchas
140
+
141
+ - **Ordering guarantees** — most message brokers guarantee order per partition/key, not globally
142
+ - **Eventual consistency** — read model may be stale; handle "not found yet" in consumers
143
+ - **Event versioning** — changing event schemas without migration breaks consumers
144
+ - **Debugging** — tracing an operation across async events is harder than a synchronous call stack
145
+ - **Two-phase commit trap** — don't try to atomically write to DB + publish event; use outbox pattern
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: microservices
3
+ description: Microservices design patterns — service boundaries, communication, resilience, and observability
4
+ category: architecture
5
+ version: "1.0"
6
+ depends_on:
7
+ - domain-driven-design
8
+ - event-driven
9
+ compatible_with:
10
+ - docker
11
+ - kubernetes
12
+ - rest-api
13
+ - grpc
14
+ ---
15
+
16
+ # Microservices Architecture
17
+
18
+ ## Overview
19
+
20
+ Microservices decompose an application into small, independently deployable services organized around business capabilities. Each service owns its data and communicates via APIs or events.
21
+
22
+ ## Service Design
23
+
24
+ ### Service Boundaries
25
+
26
+ ```
27
+ ✅ Good boundaries (aligned with business capabilities):
28
+ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐
29
+ │ Users │ │ Orders │ │ Payments │ │ Shipping │
30
+ │ │ │ │ │ │ │ │
31
+ │ - Auth │ │ - Cart │ │ - Charge │ │ - Track │
32
+ │ - Profile│ │ - Checkout│ │ - Refund │ │ - Fulfill │
33
+ │ - Prefs │ │ - History │ │ - Ledger │ │ - Label │
34
+ └──────────┘ └───────────┘ └──────────┘ └───────────┘
35
+
36
+ ❌ Bad boundaries (tech layers):
37
+ ┌──────────┐ ┌───────────┐ ┌──────────┐
38
+ │ Frontend │ │ Backend │ │ Database │ ← Not microservices
39
+ └──────────┘ └───────────┘ └──────────┘
40
+ ```
41
+
42
+ ### Communication
43
+
44
+ ```
45
+ Synchronous (request/response):
46
+ REST API — Simple CRUD, public APIs
47
+ gRPC — Internal service-to-service, streaming
48
+
49
+ Asynchronous (event-driven):
50
+ Message Queue (RabbitMQ, SQS) — Task processing, commands
51
+ Event Stream (Kafka) — Event sourcing, data sync
52
+ ```
53
+
54
+ ## Core Patterns
55
+
56
+ ### API Gateway
57
+
58
+ ```yaml
59
+ # Route external requests to internal services
60
+ routes:
61
+ /api/users/*: upstream: http://user-service:3000
62
+ /api/orders/*: upstream: http://order-service:3000
63
+ /api/payments/*: upstream: http://payment-service:3000
64
+
65
+ # Cross-cutting concerns at the gateway:
66
+ # - Authentication / JWT validation
67
+ # - Rate limiting
68
+ # - Request logging
69
+ # - Response caching
70
+ ```
71
+
72
+ ### Service Communication
73
+
74
+ ```typescript
75
+ // Sync: REST with circuit breaker
76
+ import CircuitBreaker from 'opossum';
77
+
78
+ const paymentBreaker = new CircuitBreaker(
79
+ async (orderId: string) => {
80
+ return fetch(`http://payment-service/charges/${orderId}`);
81
+ },
82
+ { timeout: 3000, errorThresholdPercentage: 50, resetTimeout: 30000 }
83
+ );
84
+
85
+ // Fallback when circuit is open
86
+ paymentBreaker.fallback(() => ({ status: 'pending', message: 'Payment service unavailable' }));
87
+ ```
88
+
89
+ ### Saga Pattern (distributed transactions)
90
+
91
+ ```
92
+ Choreography (events):
93
+ OrderCreated → PaymentCharged → InventoryReserved → ShipmentScheduled
94
+ ↓ (if fails)
95
+ OrderCreated → PaymentCharged → InventoryFailed → PaymentRefunded → OrderCancelled
96
+
97
+ Orchestration (central coordinator):
98
+ OrderSaga:
99
+ 1. Create order (pending)
100
+ 2. Charge payment → if fails → cancel order
101
+ 3. Reserve inventory → if fails → refund payment → cancel order
102
+ 4. Schedule shipment → if fails → release inventory → refund → cancel
103
+ 5. Mark order complete
104
+ ```
105
+
106
+ ### Service Mesh / Observability
107
+
108
+ ```yaml
109
+ # Distributed tracing headers (propagate across services)
110
+ X-Request-ID: unique-per-request
111
+ X-Correlation-ID: unique-per-user-action
112
+
113
+ # Health check endpoint (every service)
114
+ GET /health
115
+ {
116
+ "status": "healthy",
117
+ "version": "1.2.3",
118
+ "uptime": 86400,
119
+ "dependencies": {
120
+ "database": "connected",
121
+ "cache": "connected",
122
+ "payment-service": "healthy"
123
+ }
124
+ }
125
+ ```
126
+
127
+ ## Best Practices
128
+
129
+ 1. **Database per service** — no shared databases; communicate via APIs/events
130
+ 2. **Smart endpoints, dumb pipes** — business logic in services, not in the messaging layer
131
+ 3. **Design for failure** — circuit breakers, retries with backoff, fallbacks
132
+ 4. **Health checks** — every service exposes `/health` with dependency status
133
+ 5. **Distributed tracing** — propagate correlation IDs through all service calls
134
+ 6. **Independent deployability** — each service has its own CI/CD pipeline
135
+ 7. **Start with a monolith** — extract services when team/domain boundaries become clear
136
+
137
+ ## Gotchas
138
+
139
+ - **Distributed monolith** — services that must deploy together aren't microservices
140
+ - **Data consistency** — no ACID across services; use sagas and eventual consistency
141
+ - **Network unreliability** — every service call can fail, timeout, or return stale data
142
+ - **Operational complexity** — monitoring, logging, tracing across N services is hard
143
+ - **Service discovery** — services need to find each other; use DNS, Consul, or Kubernetes services
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: tla-precheck
3
+ description: "Formal design verification using TLA+ model checking via tla-precheck. Use when a feature involves state machines, lifecycle transitions, concurrent state mutations, or invariants that must hold across all interleavings."
4
+ category: architecture
5
+ compatible_with:
6
+ - clean-architecture
7
+ - domain-driven-design
8
+ - event-driven
9
+ depends_on: []
10
+ ---
11
+
12
+ # TLA+ PreCheck — Formal Design Verification
13
+
14
+ ## Overview
15
+
16
+ [tla-precheck](https://github.com/kingbootoshi/tla-precheck) translates a TypeScript state machine DSL (`.machine.ts`) into TLA+ specifications, then runs the TLC model checker to exhaustively verify invariants and graph equivalence. Odin integrates it as an opt-in design verification step in the Architect → Guardian flow.
17
+
18
+ ## When to Use
19
+
20
+ Write a `.machine.ts` file when the feature involves:
21
+
22
+ - **Entity lifecycle management** — status transitions, state machines with guard conditions
23
+ - **Concurrent operations on shared state** — multiple actors mutating the same resource
24
+ - **Financial/billing state changes** — charges, refunds, subscriptions where invariant violations cause real damage
25
+ - **Distributed workflow coordination** — where guard conditions are scattered across files/services
26
+ - **Any invariant that must hold across all possible interleavings** — not just the happy path
27
+
28
+ Complexity level (L1/L2/L3) is a secondary signal. Even a small L1 feature can contain a high-risk lifecycle invariant.
29
+
30
+ ## The `.machine.ts` DSL
31
+
32
+ A machine definition describes states, events, transitions, and invariants:
33
+
34
+ ```typescript
35
+ import { defineMachine } from 'tla-precheck';
36
+
37
+ export default defineMachine({
38
+ moduleName: 'AgentRuns',
39
+
40
+ constants: {
41
+ Users: { type: 'set', values: ['u1', 'u2'] },
42
+ MaxConcurrent: { type: 'number', value: 1 },
43
+ },
44
+
45
+ state: {
46
+ runs: { type: 'map', keys: 'Users', values: ['idle', 'queued', 'running', 'completed'] },
47
+ },
48
+
49
+ init: {
50
+ runs: { u1: 'idle', u2: 'idle' },
51
+ },
52
+
53
+ actions: {
54
+ QueueRun: {
55
+ guard: (s) => Object.values(s.runs).some((v) => v === 'idle'),
56
+ update: (s) => {
57
+ const user = Object.keys(s.runs).find((u) => s.runs[u] === 'idle')!;
58
+ s.runs[user] = 'queued';
59
+ },
60
+ },
61
+ StartRun: {
62
+ guard: (s) => {
63
+ const running = Object.values(s.runs).filter((v) => v === 'running').length;
64
+ return running < MaxConcurrent && Object.values(s.runs).some((v) => v === 'queued');
65
+ },
66
+ update: (s) => {
67
+ const user = Object.keys(s.runs).find((u) => s.runs[u] === 'queued')!;
68
+ s.runs[user] = 'running';
69
+ },
70
+ },
71
+ CompleteRun: {
72
+ guard: (s) => Object.values(s.runs).some((v) => v === 'running'),
73
+ update: (s) => {
74
+ const user = Object.keys(s.runs).find((u) => s.runs[u] === 'running')!;
75
+ s.runs[user] = 'completed';
76
+ },
77
+ },
78
+ },
79
+
80
+ invariants: {
81
+ AtMostOneRunning: (s) =>
82
+ Object.values(s.runs).filter((v) => v === 'running').length <= MaxConcurrent,
83
+ },
84
+ });
85
+ ```
86
+
87
+ ## Odin Integration
88
+
89
+ ### Architect Phase (Phase 3)
90
+
91
+ 1. Identify state flows in the spec that need formal verification
92
+ 2. Write a `.machine.ts` file for each critical flow
93
+ 3. Document in spec section "4.6 State Machine Verification"
94
+ 4. Ask the orchestrator to call `odin.verify_design` for each machine
95
+ 5. If proof fails → fix the **design** (the DSL), not patch around it
96
+ 6. Loop until all machines return `status: VERIFIED`
97
+ 7. Record aggregated results as one `design_verification` phase artifact
98
+
99
+ ### Guardian Phase (Phase 4)
100
+
101
+ Guardian reviews proof results in the Technical Soundness perspective:
102
+
103
+ - All machines `VERIFIED` → supports **Good**
104
+ - Any machine `VIOLATION` → **Blocking** (design must be fixed)
105
+ - State flows in spec but no proof run → **Needs Work**
106
+ - Tool not configured/available → **N/A**
107
+
108
+ ### Configuration
109
+
110
+ ```yaml
111
+ # .odin/config.yaml
112
+ formal_verification:
113
+ provider: tla-precheck # or "none" (default)
114
+ timeout_seconds: 120
115
+ ```
116
+
117
+ ### Requirements
118
+
119
+ - Java 17+ (for the TLC model checker)
120
+ - `tla-precheck` installed as a devDependency in the target project
121
+
122
+ ```bash
123
+ npm install -D tla-precheck
124
+ ```
125
+
126
+ ## Verification Status Codes
127
+
128
+ | Status | Meaning |
129
+ |--------|---------|
130
+ | `VERIFIED` | Proof passed, invariants hold, graph equivalence confirmed |
131
+ | `VIOLATION` | Invariant violation found — design bug |
132
+ | `INVALID_MODEL` | DSL parse error or malformed machine — fix the `.machine.ts`, not the design |
133
+ | `TIMEOUT` | TLC exceeded time budget — simplify the state space or increase timeout |
134
+ | `UNAVAILABLE` | Java or tla-precheck not installed |
135
+ | `NOT_CONFIGURED` | `formal_verification.provider` is `none` |
136
+ | `INTERNAL_ERROR` | Unexpected failure in tla-precheck |
137
+
138
+ ## Design Loop
139
+
140
+ ```
141
+ Write .machine.ts
142
+
143
+
144
+ odin.verify_design
145
+
146
+ ┌───┴───┐
147
+ │ │
148
+ VERIFIED VIOLATION
149
+ │ │
150
+ ▼ ▼
151
+ Done Read counter-example
152
+ Fix the DESIGN (DSL)
153
+ Re-run verification
154
+ └──────────────┘
155
+ ```
156
+
157
+ The key insight: when proof fails, **fix the design**, not the code. The `.machine.ts` IS the design. If you can't make the invariant hold in the model, you can't make it hold in the implementation.
158
+
159
+ ## Best Practices
160
+
161
+ - **Keep state spaces small** — use 2-3 constants per set, not production-scale values. TLC checks all interleavings exhaustively.
162
+ - **Name invariants descriptively** — `AtMostOneRunning` not `Inv1`. Guardian reviews these names.
163
+ - **One machine per concern** — don't model your entire system in one file. Model the billing flow, the auth flow, the workflow transitions separately.
164
+ - **Pin the version** — `tla-precheck` is early-stage (1 contributor, no releases). Pin exact version in `package.json`.
165
+ - **Check `equivalent`** — proof passing but `equivalent: false` means your TypeScript interpreter diverges from the TLA+ spec. This is a bug in the DSL, not the model checker.
166
+
167
+ ## References
168
+
169
+ - [tla-precheck GitHub](https://github.com/kingbootoshi/tla-precheck)
170
+ - [TLA+ Learning Resources](https://lamport.azurewebsites.net/tla/learning.html)
171
+ - [Odin Architect Agent — Section 4.6](../../../agents/definitions/architect.md)