diff --git a/src/agent_extensions/skills/implement_design_doc_java/README.md b/src/agent_extensions/skills/implement_design_doc_java/README.md new file mode 100644 index 0000000..5056518 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/README.md @@ -0,0 +1,75 @@ +# implement-design-doc-java + +Skill that translates DesignDoc JSON contracts into Java domain model code, adapting to the target project's coding style. + +## Installation + +```bash +claude install-skill src/agent_extensions/skills/implement_design_doc_java/implement_design_doc_java.skill +``` + +Or copy `SKILL.md` and `references/` into your project's `.claude/skills/` directory. + +## Usage + +Provide a DesignDoc JSON file (conforming to `contracts/design-doc-schema.json`) and point to your Java project: + +``` +Implement the DesignDoc JSON into my Java project. +DesignDoc: path/to/design-doc.json +Project: path/to/java-project/ +``` + +The skill will: +1. Read existing code to detect project conventions (Lombok, records, Either, sealed interfaces, facade pattern, etc.) +2. Parse the DesignDoc JSON and resolve building block references +3. Search for existing shared types (IDs, Money) and import them instead of creating duplicates +4. Generate Java classes in dependency order: value objects → events → entities → aggregates → services → repositories → application services +5. Match the project's package structure, error handling, encapsulation, and naming conventions + +## What it handles + +- **Building block types**: aggregate, entity, value_object, domain_event, domain_command, domain_query, domain_service, application_service, repository, factory, external_integration +- **Style adaptation**: Lombok, plain Java, Java records, sealed interfaces, Vavr Either, Spring Configuration, facade pattern, command/handler pattern, service-per-use-case +- **Reuse detection**: finds existing classes by name before creating new ones +- **Encapsulation**: entities within aggregates get package-private visibility +- **Cross-module**: places building blocks in correct packages based on bounded context / module hierarchy + +## DesignDoc JSON schema + +The input JSON follows the DesignDoc schema with these top-level fields: + +```json +{ + "actors": [], + "businessGoals": [], + "domainConcepts": [], + "rules": [], + "qualityAttributes": [], + "boundedContexts": [], + "buildingBlocks": [], + "useCases": [], + "scenarios": [] +} +``` + +Each `buildingBlock` has `id`, `name`, `type`, `description`, `properties`, and `behaviours`. Behaviours reference other building blocks by ID (`input`/`output`) and rules by ID. + +See `references/designdoc_mapping.md` for detailed mapping rules from JSON to Java. + +## Eval results + +Tested on 4 discriminating scenarios with neutral prompts (no hints): + +| Scenario | With Skill | Without Skill | Delta | +|---|---|---|---| +| Pricing (Plain Java + Vavr) | 9/9 | 7/9 | +22% | +| Payroll (Mixed patterns) | 9/9 | 7/9 | +22% | +| Receiving (Unusual conventions) | 8/8 | 6/8 | +25% | +| Returns (Cross-module) | 9/9 | 9/9 | 0% | +| **Total** | **35/35 (100%)** | **29/35 (83%)** | **+17pp** | + +Key skill advantages over baseline Claude: +- Reuses existing shared types instead of creating duplicates +- Detects package-private encapsulation for entities within aggregates +- Follows service-per-use-case pattern when project uses it diff --git a/src/agent_extensions/skills/implement_design_doc_java/SKILL.md b/src/agent_extensions/skills/implement_design_doc_java/SKILL.md new file mode 100644 index 0000000..1261ade --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/SKILL.md @@ -0,0 +1,132 @@ +--- +name: implement-design-doc-java +description: Generate or update Java domain model code from a design document JSON file. Use this skill whenever the user asks to implement, generate, or create Java classes from a design doc, design document, design specification, or any JSON file that describes domain building blocks (aggregates, entities, value objects, domain events, repositories, services). Also use when the user wants to turn a domain model specification into Java code, sync Java code with a design contract, or implement building blocks from a JSON description. Trigger on phrases like "implement the design doc", "generate Java from this JSON", "create domain classes from the spec", "implement the building blocks", "turn this design into code", even if the user doesn't say "DesignDoc" exactly — any JSON describing a domain model with bounded contexts, modules, or building blocks qualifies. +--- + +# Implement Design Doc (Java) + +You are implementing Java domain model code from a DesignDoc JSON contract. The contract is the single source of truth for what the code must contain. Your job is to produce Java code that faithfully represents every building block, property, behaviour, and relationship described in the contract, while matching the style and conventions of the target project. + +## Core Principles + +1. **DesignDoc JSON is authoritative.** Every building block, property, behaviour, and structural relationship in the JSON must be reflected in code. Never invent domain concepts not in the contract. If something is ambiguous, ask the user. + +2. **Adapt to the project's style.** Before writing any code, read existing sources to detect the project's conventions (see Style Detection below). The contract dictates *what* exists; the project dictates *how* it looks. + +3. **Reuse existing classes — never duplicate.** When a building block's `description` says "Already exists in the project" or a class with that name already exists in the codebase, import it from its current location. Do not create a new copy in a different package. Search the project for the class before creating it. This is critical for types like shared value objects (IDs, Money) that are used across modules. + +4. **Replicate the project's exact patterns.** When the project uses a specific pattern for domain events (e.g., `sealed interface Event permits ...` with inner records), aggregates (e.g., `pendingEvents` + `flushEvents()`), or use case coordination (e.g., facade + package-private services + @Configuration), replicate that exact pattern. Do not substitute a different pattern even if it's valid DDD — the goal is consistency with the existing codebase. + +5. **Implement domain model only.** Infrastructure implementations (persistence, messaging, HTTP) are out of scope unless the contract explicitly includes `external_integration` building blocks. Repository interfaces are in scope; their implementations are not. + +6. **Tests are out of scope.** Other skills or project conventions handle testing. Focus exclusively on production domain code. + +## Input + +A DesignDoc JSON conforming to the schema at `contracts/design-doc-schema.json`. The JSON contains: + +- `boundedContexts` with `modules` and `buildingBlocks` references +- `buildingBlocks` — flat list, each with `type`, `properties`, `behaviours` +- `rules` — business rules referenced by behaviours +- `useCases` — application-level orchestrations referencing building blocks +- `actors`, `businessGoals`, `domainConcepts`, `scenarios` — contextual information + +See [designdoc_mapping.md](references/designdoc_mapping.md) for the complete mapping rules from JSON to Java. + +## Workflow + +### 1. Detect project style + +Before writing any code, read the target project to understand its conventions: + +1. **Find the build system** — look for `pom.xml` or `build.gradle` to determine Java version and dependencies +2. **Read CLAUDE.md** and any project-level instructions — they may override default patterns +3. **Sample 3-5 existing domain classes** across different building block types (aggregates, value objects, services) and note: + - Package structure (how bounded contexts and modules map to packages) + - Naming conventions (class names, method names, field access) + - Annotation usage (Lombok, JPA, custom annotations, or none) + - Error handling pattern (exceptions, Result/Either monads, Optional) + - **Domain event pattern** — this is critical: does the project use a marker interface? sealed interfaces with inner records? separate event classes with @Value? a pendingEvents list flushed from aggregates? Copy the exact mechanism. + - Value object style (records, final classes, Lombok @Value) + - Encapsulation style (package-private vs public, accessor methods vs direct fields) + - Constructor patterns (static factories, builders, plain constructors) + - Repository interface style (method naming, return types) + - **Application service / use case coordination pattern** — does the project use: facades delegating to package-private services (with Spring @Configuration wiring)? command/handler pairs (Command + CommandHandler)? service-per-use-case classes? This determines how you structure the application layer. + +4. **Search for existing shared types** — before creating any value object, search the entire project for a class with that name. Especially for ID types and Money-like types which are commonly shared across modules. If found, import it — do not create a duplicate. + +Document detected conventions in a mental checklist before proceeding. When the project has no existing code in a category, fall back to standard Java DDD patterns. + +### 2. Resolve the DesignDoc + +Parse the JSON and build a mental model: + +1. **Map ownership** — for each `buildingBlock`, determine which `boundedContext` and `module` owns it (via `buildingBlocks` id references in contexts/modules) +2. **Resolve references** — behaviours reference other building blocks by id (in `input`/`output` fields) and rules by id. Resolve these to actual names and types. +3. **Identify dependencies** — which building blocks reference which others, to determine implementation order +4. **Detect what already exists** — search the codebase for classes matching building block names. Existing classes need modification, missing ones need creation. + +### 3. Plan implementation batches + +Group by dependency order: + +1. **Value Objects** — no domain dependencies, implement first +2. **Domain Events** — typically simple immutable records +3. **Entities** — may depend on value objects +4. **Aggregates** — depend on entities, value objects, events +5. **Domain Services** — coordinate multiple domain objects +6. **Repositories** — interfaces referencing aggregate roots +7. **Application Services / Use Cases** — orchestrate everything above +8. **Factories** — complex creation logic + +### 4. Implement or modify code + +For each building block, follow the mapping rules in [designdoc_mapping.md](references/designdoc_mapping.md). + +**For new classes:** Create in the correct package following the project's package structure conventions. Match the style detected in step 1. + +**For existing classes:** Compare the DesignDoc contract with what exists: +- Missing properties → add fields +- Missing behaviours → add methods +- Changed types → update types +- Missing building blocks within an aggregate → add inner/companion classes +- Present edits as minimal diffs — don't rewrite files unnecessarily + +**Key rules for all code:** +- Every `property` in the JSON becomes a field in the class +- Every `behaviour` becomes a method. The behaviour's `description` informs the method's purpose. `input` and `output` references determine parameter and return types. +- `rules` referenced by a behaviour represent business logic that the method must enforce +- The `description` field on building blocks informs relationships and responsibilities — parse it for mentions of ownership, containment, and collaboration patterns + +### 5. Verify completeness + +After implementation, verify that: +- Every building block in the JSON has a corresponding class +- Every property is represented as a field +- Every behaviour is represented as a method +- Every rule referenced by a behaviour is enforced in the method's logic +- Package structure reflects the bounded context / module hierarchy +- Use cases are wired to the correct building blocks + +Present a summary to the user mapping each DesignDoc element to its code location. + +## Style Detection Cheat Sheet + +| What to detect | Where to look | How it affects output | +|---|---|---| +| Java version | `pom.xml` / `build.gradle` | Records (16+), sealed classes (17+), pattern matching | +| Lombok | `lombok.config`, imports | Use @Value, @Builder, @RequiredArgsConstructor if project uses them | +| Error handling | Aggregate methods, service returns | Match: exceptions, Result monad, Either, checked exceptions | +| Event pattern | Existing domain events | Match: records, classes, marker interfaces, event bus | +| Package layout | src/main/java structure | Match: flat vs layered (domain/application/infrastructure) | +| Encapsulation | Access modifiers on existing classes | Match: package-private children, public API surface | +| Immutability | Field declarations, setters | Match: final fields, records, defensive copies | +| Constructor style | Existing constructors | Match: static factories, builders, plain | +| Repository location | Existing repository interfaces | Match: separate file vs **nested interface inside aggregate** (e.g., `Order.Repository`) | +| DI framework | Spring/Guice/CDI annotations | Match: @Component, @Service, configuration classes | + +## Scope + +**IN:** Domain model classes, value objects, aggregates, entities, domain events, domain services, application services, repository interfaces, factory classes — all derived from DesignDoc JSON. + +**OUT:** Tests, infrastructure implementations, UI, database migrations, build configuration, design changes. If the contract seems wrong, ask — don't fix it silently. diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/evals.json b/src/agent_extensions/skills/implement_design_doc_java/evals/evals.json new file mode 100644 index 0000000..b6895ef --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/evals.json @@ -0,0 +1,25 @@ +{ + "skill_name": "implement-design-doc-java", + "evals": [ + { + "id": 1, + "prompt": "Implement the DesignDoc JSON into my Java project. DesignDoc: evals/fixtures/designdoc-pricing.json, project: evals/fixtures/style-plain-java/", + "files": ["evals/fixtures/designdoc-pricing.json", "evals/fixtures/style-plain-java/"] + }, + { + "id": 2, + "prompt": "Implement the DesignDoc JSON into my Java project. DesignDoc: evals/fixtures/designdoc-payroll.json, project: evals/fixtures/style-mixed-patterns/", + "files": ["evals/fixtures/designdoc-payroll.json", "evals/fixtures/style-mixed-patterns/"] + }, + { + "id": 3, + "prompt": "Implement the DesignDoc JSON into my Java project. DesignDoc: evals/fixtures/designdoc-receiving.json, project: evals/fixtures/style-unusual-conventions/", + "files": ["evals/fixtures/designdoc-receiving.json", "evals/fixtures/style-unusual-conventions/"] + }, + { + "id": 4, + "prompt": "Implement the DesignDoc JSON into my Java project. DesignDoc: evals/fixtures/designdoc-returns.json, project: evals/fixtures/style-cross-module/", + "files": ["evals/fixtures/designdoc-returns.json", "evals/fixtures/style-cross-module/"] + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-order-extended.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-order-extended.json new file mode 100644 index 0000000..2f89938 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-order-extended.json @@ -0,0 +1,442 @@ +{ + "actors": [ + { + "id": "actor-1", + "name": "Customer", + "description": "Places and manages orders" + } + ], + "businessGoals": [ + { + "id": "bg-1", + "name": "Flexible Order Management", + "description": "Allow customers to cancel orders and apply discounts" + } + ], + "domainConcepts": [ + { + "id": "dc-1", + "name": "Order", + "description": "A customer's purchase request" + }, + { + "id": "dc-2", + "name": "Discount Code", + "description": "A promotional code that reduces order total" + }, + { + "id": "dc-3", + "name": "Shipping Address", + "description": "Delivery destination for the order" + } + ], + "rules": [ + { + "id": "rule-1", + "ruleType": "State change", + "description": "Only placed orders can be cancelled" + }, + { + "id": "rule-2", + "ruleType": "Consistency", + "description": "Cannot add lines to a non-draft order" + }, + { + "id": "rule-3", + "ruleType": "Consistency", + "description": "Cannot place an empty order" + }, + { + "id": "rule-4", + "ruleType": "Computation", + "description": "Discounted total is order total multiplied by (1 - discount percentage / 100)" + }, + { + "id": "rule-5", + "ruleType": "Structure", + "description": "Discount percentage must be between 0 and 100" + }, + { + "id": "rule-6", + "ruleType": "Structure", + "description": "Shipping address must have street, city, and postalCode" + }, + { + "id": "rule-7", + "ruleType": "State change", + "description": "Shipping address can only be set on draft orders" + } + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Orders", + "description": "Order management bounded context", + "modules": [], + "buildingBlocks": [ + "bb-1", + "bb-2", + "bb-3", + "bb-4", + "bb-5", + "bb-6", + "bb-7", + "bb-8", + "bb-9", + "bb-10", + "bb-11" + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "Order", + "type": "aggregate", + "description": "Aggregate root for customer orders. Contains OrderLine entities.", + "properties": [ + { + "name": "id", + "type": "OrderId" + }, + { + "name": "customerId", + "type": "CustomerId" + }, + { + "name": "lines", + "type": "List" + }, + { + "name": "status", + "type": "OrderStatus" + }, + { + "name": "createdAt", + "type": "Instant" + }, + { + "name": "shippingAddress", + "type": "ShippingAddress" + }, + { + "name": "discountCode", + "type": "DiscountCode" + } + ], + "behaviours": [ + { + "name": "create", + "description": "Creates a new draft order for a customer.", + "input": [ + "bb-3" + ], + "output": [], + "rules": [] + }, + { + "name": "addLine", + "description": "Adds a product line to the order.", + "input": [], + "output": [], + "rules": [ + "rule-2" + ] + }, + { + "name": "place", + "description": "Places the order.", + "input": [], + "output": [], + "rules": [ + "rule-3" + ] + }, + { + "name": "cancel", + "description": "Cancels a placed order.", + "input": [], + "output": [ + "bb-8" + ], + "rules": [ + "rule-1" + ] + }, + { + "name": "applyDiscount", + "description": "Applies a discount code to the order.", + "input": [ + "bb-6" + ], + "output": [], + "rules": [ + "rule-5" + ] + }, + { + "name": "setShippingAddress", + "description": "Sets the shipping address for the order.", + "input": [ + "bb-7" + ], + "output": [], + "rules": [ + "rule-7" + ] + }, + { + "name": "discountedTotal", + "description": "Calculates total after discount.", + "input": [], + "output": [ + "bb-5" + ], + "rules": [ + "rule-4" + ] + } + ] + }, + { + "id": "bb-2", + "name": "OrderId", + "type": "value_object", + "description": "Unique identifier for an order.", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "CustomerId", + "type": "value_object", + "description": "Unique identifier for a customer.", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-4", + "name": "OrderLine", + "type": "entity", + "description": "A line item in an order.", + "properties": [ + { + "name": "productName", + "type": "String" + }, + { + "name": "quantity", + "type": "int" + }, + { + "name": "unitPrice", + "type": "Money" + } + ], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "Money", + "type": "value_object", + "description": "Monetary amount.", + "properties": [ + { + "name": "amount", + "type": "BigDecimal" + }, + { + "name": "currency", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-6", + "name": "DiscountCode", + "type": "value_object", + "description": "A promotional discount code with a percentage.", + "properties": [ + { + "name": "code", + "type": "String" + }, + { + "name": "percentage", + "type": "int" + } + ], + "behaviours": [ + { + "name": "applyTo", + "description": "Applies this discount to a money amount", + "input": [ + "bb-5" + ], + "output": [ + "bb-5" + ], + "rules": [ + "rule-4" + ] + } + ] + }, + { + "id": "bb-7", + "name": "ShippingAddress", + "type": "value_object", + "description": "Delivery address for an order.", + "properties": [ + { + "name": "street", + "type": "String" + }, + { + "name": "city", + "type": "String" + }, + { + "name": "postalCode", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "OrderCancelled", + "type": "domain_event", + "description": "Emitted when an order is cancelled.", + "properties": [ + { + "name": "orderId", + "type": "OrderId" + }, + { + "name": "cancelledAt", + "type": "Instant" + } + ], + "behaviours": [] + }, + { + "id": "bb-9", + "name": "OrderStatus", + "type": "value_object", + "description": "Lifecycle state of an order: Draft, Placed, Cancelled.", + "properties": [ + { + "name": "value", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-10", + "name": "OrderRepository", + "type": "repository", + "description": "Repository for orders.", + "properties": [], + "behaviours": [ + { + "name": "save", + "description": "Persists an order", + "input": [ + "bb-1" + ], + "output": [], + "rules": [] + }, + { + "name": "findById", + "description": "Finds an order by id", + "input": [ + "bb-2" + ], + "output": [ + "bb-1" + ], + "rules": [] + } + ] + }, + { + "id": "bb-11", + "name": "CancelOrderService", + "type": "application_service", + "description": "Orchestrates the cancel order use case.", + "properties": [], + "behaviours": [ + { + "name": "cancel", + "description": "Loads an order and cancels it", + "input": [ + "bb-2" + ], + "output": [ + "bb-8" + ], + "rules": [ + "rule-1" + ] + } + ] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Cancel Order", + "actor": "actor-1", + "type": "Command", + "description": "Customer cancels a placed order", + "businessGoal": "bg-1", + "input": [ + "bb-2" + ], + "output": [ + "bb-8" + ], + "usedBuildingBlocks": [ + "bb-1", + "bb-10", + "bb-11" + ], + "rules": [ + "rule-1" + ], + "scenarios": [ + { + "name": "Successful cancellation", + "description": "Placed order is cancelled", + "given": "A placed order", + "when": "Customer cancels it", + "then": "Order status becomes Cancelled, OrderCancelled event emitted" + }, + { + "name": "Draft order cannot be cancelled", + "description": "Only placed orders can be cancelled", + "given": "A draft order", + "when": "Customer tries to cancel", + "then": "Cancellation is rejected" + } + ], + "qualities": [] + } + ], + "scenarios": [] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-payroll.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-payroll.json new file mode 100644 index 0000000..b14b272 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-payroll.json @@ -0,0 +1,362 @@ +{ + "actors": [ + { + "id": "actor-1", + "name": "HR Manager", + "description": "Manages payroll runs for employees" + } + ], + "businessGoals": [ + { + "id": "bg-1", + "name": "Accurate Payroll", + "description": "Calculate and execute payroll correctly for all employees" + } + ], + "domainConcepts": [ + { + "id": "dc-1", + "name": "Payroll Run", + "description": "A monthly execution of salary payments for a set of employees" + }, + { + "id": "dc-2", + "name": "Salary", + "description": "Base monthly salary for an employee" + }, + { + "id": "dc-3", + "name": "Payslip", + "description": "Individual payment record for one employee in a payroll run" + } + ], + "rules": [ + { + "id": "rule-1", + "ruleType": "Consistency", + "description": "A payroll run can only be executed once \u2014 no double payments" + }, + { + "id": "rule-2", + "ruleType": "Structure", + "description": "Salary must be a positive amount" + }, + { + "id": "rule-3", + "ruleType": "Computation", + "description": "Net pay equals salary minus deductions" + }, + { + "id": "rule-4", + "ruleType": "State change", + "description": "An executed payroll run cannot be modified" + }, + { + "id": "rule-5", + "ruleType": "Consistency", + "description": "A payroll run must contain at least one payslip before execution" + } + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "HR", + "description": "Human resources bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Payroll", + "description": "Manages payroll calculation and execution", + "buildingBlocks": [ + "bb-1", + "bb-2", + "bb-3", + "bb-4", + "bb-5", + "bb-6", + "bb-7", + "bb-8", + "bb-9" + ] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "PayrollRun", + "type": "aggregate", + "description": "Aggregate root managing a monthly payroll execution. Contains Payslip entities.", + "properties": [ + { + "name": "id", + "type": "PayrollRunId" + }, + { + "name": "month", + "type": "YearMonth" + }, + { + "name": "payslips", + "type": "List" + }, + { + "name": "status", + "type": "PayrollStatus" + } + ], + "behaviours": [ + { + "name": "create", + "description": "Creates a new payroll run for a given month", + "input": [], + "output": [], + "rules": [] + }, + { + "name": "addPayslip", + "description": "Adds a payslip for an employee to this payroll run", + "input": [ + "bb-4", + "bb-5" + ], + "output": [], + "rules": [ + "rule-2", + "rule-4" + ] + }, + { + "name": "execute", + "description": "Executes the payroll run, marking all payslips as paid", + "input": [], + "output": [ + "bb-7" + ], + "rules": [ + "rule-1", + "rule-4", + "rule-5" + ] + } + ] + }, + { + "id": "bb-2", + "name": "PayrollRunId", + "type": "value_object", + "description": "Unique identifier for a payroll run", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "PayrollStatus", + "type": "value_object", + "description": "Lifecycle state of a payroll run: Draft, Executed", + "properties": [ + { + "name": "value", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-4", + "name": "EmployeeId", + "type": "value_object", + "description": "Unique identifier for an employee.", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "Salary", + "type": "value_object", + "description": "Monthly salary amount", + "properties": [ + { + "name": "grossAmount", + "type": "BigDecimal" + }, + { + "name": "deductions", + "type": "BigDecimal" + } + ], + "behaviours": [ + { + "name": "netPay", + "description": "Calculates net pay after deductions", + "input": [], + "output": [], + "rules": [ + "rule-3" + ] + } + ] + }, + { + "id": "bb-6", + "name": "Payslip", + "type": "entity", + "description": "Individual payment record within a payroll run. Identity is employeeId within the run.", + "properties": [ + { + "name": "employeeId", + "type": "EmployeeId" + }, + { + "name": "salary", + "type": "Salary" + }, + { + "name": "paid", + "type": "boolean" + } + ], + "behaviours": [ + { + "name": "markPaid", + "description": "Marks this payslip as paid", + "input": [], + "output": [], + "rules": [] + } + ] + }, + { + "id": "bb-7", + "name": "PayrollExecuted", + "type": "domain_event", + "description": "Emitted when a payroll run is executed", + "properties": [ + { + "name": "payrollRunId", + "type": "PayrollRunId" + }, + { + "name": "month", + "type": "YearMonth" + }, + { + "name": "totalNetPay", + "type": "BigDecimal" + }, + { + "name": "executedAt", + "type": "Instant" + } + ], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "PayrollRunRepository", + "type": "repository", + "description": "Repository for persisting and retrieving PayrollRun aggregates", + "properties": [], + "behaviours": [ + { + "name": "save", + "description": "Persists a payroll run", + "input": [ + "bb-1" + ], + "output": [], + "rules": [] + }, + { + "name": "findById", + "description": "Finds a payroll run by id", + "input": [ + "bb-2" + ], + "output": [ + "bb-1" + ], + "rules": [] + } + ] + }, + { + "id": "bb-9", + "name": "ExecutePayrollService", + "type": "application_service", + "description": "Orchestrates payroll execution use case", + "properties": [], + "behaviours": [ + { + "name": "execute", + "description": "Loads the payroll run and executes it", + "input": [ + "bb-2" + ], + "output": [ + "bb-7" + ], + "rules": [ + "rule-1", + "rule-5" + ] + } + ] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Execute Payroll", + "actor": "actor-1", + "type": "Command", + "description": "HR Manager executes the monthly payroll run", + "businessGoal": "bg-1", + "input": [ + "bb-2" + ], + "output": [ + "bb-7" + ], + "usedBuildingBlocks": [ + "bb-1", + "bb-8", + "bb-9" + ], + "rules": [ + "rule-1", + "rule-5" + ], + "scenarios": [ + { + "name": "Successful execution", + "description": "Payroll with payslips is executed", + "given": "A draft payroll run with 3 payslips", + "when": "HR Manager executes the payroll", + "then": "Payroll status becomes Executed, all payslips marked paid, PayrollExecuted event emitted" + }, + { + "name": "Empty payroll rejected", + "description": "Cannot execute empty payroll", + "given": "A draft payroll run with no payslips", + "when": "HR Manager tries to execute", + "then": "Execution is rejected" + } + ], + "qualities": [] + } + ], + "scenarios": [] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-pricing.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-pricing.json new file mode 100644 index 0000000..060794d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-pricing.json @@ -0,0 +1,355 @@ +{ + "actors": [ + { + "id": "actor-1", + "name": "Product Manager", + "description": "Defines pricing for products in the catalog" + } + ], + "businessGoals": [ + { + "id": "bg-1", + "name": "Dynamic Pricing", + "description": "Support flexible pricing strategies including discounts" + } + ], + "domainConcepts": [ + { + "id": "dc-1", + "name": "Price", + "description": "Monetary value assigned to a product" + }, + { + "id": "dc-2", + "name": "Discount", + "description": "A reduction applied to the base price" + }, + { + "id": "dc-3", + "name": "Price List", + "description": "Collection of prices for products in a specific context" + } + ], + "rules": [ + { + "id": "rule-1", + "ruleType": "Consistency", + "description": "Base price must be positive" + }, + { + "id": "rule-2", + "ruleType": "Computation", + "description": "Discounted price equals base price minus discount amount, but cannot go below zero" + }, + { + "id": "rule-3", + "ruleType": "Structure", + "description": "Discount percentage must be between 0 and 100" + }, + { + "id": "rule-4", + "ruleType": "State change", + "description": "Price can only be changed for published products" + }, + { + "id": "rule-5", + "ruleType": "Consistency", + "description": "A price list must have at least one price entry" + } + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Catalog", + "description": "Product catalog bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Pricing", + "description": "Manages product pricing and discounts", + "buildingBlocks": [ + "bb-1", + "bb-2", + "bb-3", + "bb-4", + "bb-5", + "bb-6", + "bb-7", + "bb-8" + ] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "PriceList", + "type": "aggregate", + "description": "Aggregate root managing a collection of product prices. Contains PriceEntry entities.", + "properties": [ + { + "name": "id", + "type": "PriceListId" + }, + { + "name": "name", + "type": "String" + }, + { + "name": "entries", + "type": "List" + }, + { + "name": "active", + "type": "boolean" + } + ], + "behaviours": [ + { + "name": "create", + "description": "Creates a new price list with a name", + "input": [], + "output": [], + "rules": [] + }, + { + "name": "addEntry", + "description": "Adds a price entry for a product", + "input": [ + "bb-3", + "bb-4" + ], + "output": [], + "rules": [ + "rule-1" + ] + }, + { + "name": "applyDiscount", + "description": "Applies a discount to a specific product entry", + "input": [ + "bb-5" + ], + "output": [ + "bb-7" + ], + "rules": [ + "rule-2", + "rule-3" + ] + }, + { + "name": "activate", + "description": "Activates the price list for use", + "input": [], + "output": [ + "bb-8" + ], + "rules": [ + "rule-5" + ] + } + ] + }, + { + "id": "bb-2", + "name": "PriceListId", + "type": "value_object", + "description": "Unique identifier for a price list", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "PriceEntry", + "type": "entity", + "description": "A price entry for a specific product within a price list. Identity is the productId.", + "properties": [ + { + "name": "productId", + "type": "ProductId" + }, + { + "name": "basePrice", + "type": "Money" + }, + { + "name": "discount", + "type": "Discount" + } + ], + "behaviours": [ + { + "name": "effectivePrice", + "description": "Calculates the effective price after applying discount", + "input": [], + "output": [ + "bb-4" + ], + "rules": [ + "rule-2" + ] + }, + { + "name": "applyDiscount", + "description": "Applies a new discount to this entry", + "input": [ + "bb-5" + ], + "output": [], + "rules": [ + "rule-3" + ] + } + ] + }, + { + "id": "bb-4", + "name": "Money", + "type": "value_object", + "description": "Monetary amount with currency.", + "properties": [ + { + "name": "amount", + "type": "BigDecimal" + }, + { + "name": "currency", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "Discount", + "type": "value_object", + "description": "A percentage-based discount", + "properties": [ + { + "name": "percentage", + "type": "int" + } + ], + "behaviours": [ + { + "name": "applyTo", + "description": "Applies this discount to a money amount", + "input": [ + "bb-4" + ], + "output": [ + "bb-4" + ], + "rules": [ + "rule-2" + ] + } + ] + }, + { + "id": "bb-6", + "name": "PricingService", + "type": "domain_service", + "description": "Calculates the effective price for a product considering active price lists", + "properties": [], + "behaviours": [ + { + "name": "calculatePrice", + "description": "Finds the effective price for a product from the active price list", + "input": [ + "bb-1" + ], + "output": [ + "bb-4" + ], + "rules": [] + } + ] + }, + { + "id": "bb-7", + "name": "DiscountApplied", + "type": "domain_event", + "description": "Emitted when a discount is applied to a price entry", + "properties": [ + { + "name": "priceListId", + "type": "PriceListId" + }, + { + "name": "productId", + "type": "ProductId" + }, + { + "name": "discount", + "type": "Discount" + } + ], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "PriceListActivated", + "type": "domain_event", + "description": "Emitted when a price list is activated", + "properties": [ + { + "name": "priceListId", + "type": "PriceListId" + } + ], + "behaviours": [] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Apply Discount to Product", + "actor": "actor-1", + "type": "Command", + "description": "Applies a percentage discount to a product in the active price list", + "businessGoal": "bg-1", + "input": [ + "bb-5" + ], + "output": [ + "bb-7" + ], + "usedBuildingBlocks": [ + "bb-1", + "bb-3", + "bb-6" + ], + "rules": [ + "rule-2", + "rule-3" + ], + "scenarios": [ + { + "name": "Valid discount applied", + "description": "A valid discount is applied to a product", + "given": "An active price list with a product entry at base price 100", + "when": "Product manager applies 20% discount", + "then": "Effective price becomes 80 and DiscountApplied event is emitted" + }, + { + "name": "Invalid discount rejected", + "description": "Discount over 100% is rejected", + "given": "A price entry for a product", + "when": "Product manager applies 150% discount", + "then": "Discount is rejected" + } + ], + "qualities": [] + } + ], + "scenarios": [] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-receiving.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-receiving.json new file mode 100644 index 0000000..2c33622 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-receiving.json @@ -0,0 +1,169 @@ +{ + "actors": [ + {"id": "actor-1", "name": "Warehouse Worker", "description": "Processes incoming shipments at the receiving dock"} + ], + "businessGoals": [ + {"id": "bg-1", "name": "Accurate Receiving", "description": "Track all incoming goods and ensure accuracy against purchase orders"} + ], + "domainConcepts": [ + {"id": "dc-1", "name": "Receiving Note", "description": "A record of goods received at the warehouse dock"}, + {"id": "dc-2", "name": "Received Line", "description": "A line item representing a specific SKU and quantity received"}, + {"id": "dc-3", "name": "Discrepancy", "description": "A mismatch between expected and actual received quantity"} + ], + "rules": [ + {"id": "rule-1", "ruleType": "Consistency", "description": "A receiving note must have at least one line before it can be finalized"}, + {"id": "rule-2", "ruleType": "State change", "description": "A finalized receiving note cannot be modified"}, + {"id": "rule-3", "ruleType": "Structure", "description": "Received quantity must be non-negative"}, + {"id": "rule-4", "ruleType": "Computation", "description": "Discrepancy is the difference between expected quantity and actual received quantity"} + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Warehouse", + "description": "Warehouse management bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Receiving", + "description": "Handles incoming goods at the warehouse", + "buildingBlocks": ["bb-1", "bb-2", "bb-3", "bb-4", "bb-5", "bb-6", "bb-7", "bb-8"] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "ReceivingNote", + "type": "aggregate", + "description": "Aggregate root tracking goods received at the dock. Contains ReceivedLine entities.", + "properties": [ + {"name": "id", "type": "ReceivingNoteId"}, + {"name": "lines", "type": "List"}, + {"name": "status", "type": "ReceivingStatus"}, + {"name": "receivedAt", "type": "Instant"} + ], + "behaviours": [ + { + "name": "create", + "description": "Creates a new receiving note", + "input": [], + "output": [], + "rules": [] + }, + { + "name": "addLine", + "description": "Adds a received line for a SKU", + "input": ["bb-4", "bb-5"], + "output": [], + "rules": ["rule-2", "rule-3"] + }, + { + "name": "finalize", + "description": "Finalizes the receiving note, locking further changes", + "input": [], + "output": ["bb-7"], + "rules": ["rule-1", "rule-2"] + } + ] + }, + { + "id": "bb-2", + "name": "ReceivingNoteId", + "type": "value_object", + "description": "Unique identifier for a receiving note", + "properties": [{"name": "value", "type": "UUID"}], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "ReceivedLine", + "type": "entity", + "description": "A line item in a receiving note tracking actual received quantity for a SKU", + "properties": [ + {"name": "sku", "type": "Sku"}, + {"name": "expectedQuantity", "type": "Quantity"}, + {"name": "actualQuantity", "type": "Quantity"} + ], + "behaviours": [ + { + "name": "discrepancy", + "description": "Calculates the discrepancy between expected and actual quantity", + "input": [], + "output": ["bb-5"], + "rules": ["rule-4"] + } + ] + }, + { + "id": "bb-4", + "name": "Sku", + "type": "value_object", + "description": "Stock keeping unit identifier.", + "properties": [{"name": "value", "type": "String"}], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "Quantity", + "type": "value_object", + "description": "A non-negative quantity.", + "properties": [{"name": "value", "type": "int"}], + "behaviours": [] + }, + { + "id": "bb-6", + "name": "ReceivingStatus", + "type": "value_object", + "description": "Lifecycle state: Open, Finalized", + "properties": [{"name": "value", "type": "String"}], + "behaviours": [] + }, + { + "id": "bb-7", + "name": "ReceivingFinalized", + "type": "domain_event", + "description": "Emitted when a receiving note is finalized", + "properties": [ + {"name": "receivingNoteId", "type": "ReceivingNoteId"}, + {"name": "lineCount", "type": "int"}, + {"name": "finalizedAt", "type": "Instant"} + ], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "FinalizeReceivingHandler", + "type": "application_service", + "description": "Orchestrates finalization of a receiving note", + "properties": [], + "behaviours": [ + { + "name": "handle", + "description": "Loads a receiving note and finalizes it", + "input": ["bb-2"], + "output": ["bb-7"], + "rules": ["rule-1", "rule-2"] + } + ] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Finalize Receiving", + "actor": "actor-1", + "type": "Command", + "description": "Worker finalizes a receiving note after counting all goods", + "businessGoal": "bg-1", + "input": ["bb-2"], + "output": ["bb-7"], + "usedBuildingBlocks": ["bb-1", "bb-8"], + "rules": ["rule-1", "rule-2"], + "scenarios": [], + "qualities": [] + } + ], + "scenarios": [] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-returns.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-returns.json new file mode 100644 index 0000000..b96f951 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-returns.json @@ -0,0 +1,179 @@ +{ + "actors": [ + {"id": "actor-1", "name": "Customer", "description": "Requests returns for purchased items"} + ], + "businessGoals": [ + {"id": "bg-1", "name": "Hassle-free Returns", "description": "Process customer returns efficiently with refunds"} + ], + "domainConcepts": [ + {"id": "dc-1", "name": "Return Request", "description": "A customer's request to return purchased items"}, + {"id": "dc-2", "name": "Refund", "description": "Money returned to the customer after a successful return"} + ], + "rules": [ + {"id": "rule-1", "ruleType": "Consistency", "description": "Only paid orders can be returned"}, + {"id": "rule-2", "ruleType": "State change", "description": "An approved return cannot be modified"}, + {"id": "rule-3", "ruleType": "Computation", "description": "Refund amount equals the order total"}, + {"id": "rule-4", "ruleType": "Consistency", "description": "A return request must reference an existing order"} + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Sales", + "description": "Sales bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Returns", + "description": "Handles customer return requests within the sales context", + "buildingBlocks": ["bb-1", "bb-2", "bb-3", "bb-4", "bb-5"] + } + ] + }, + { + "id": "bc-2", + "name": "Fulfillment", + "description": "Fulfillment bounded context", + "modules": [ + { + "id": "mod-2", + "name": "ReturnProcessing", + "description": "Handles physical return of goods in the fulfillment context", + "buildingBlocks": ["bb-6", "bb-7", "bb-8"] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "ReturnRequest", + "type": "aggregate", + "description": "Aggregate root for a customer return request", + "properties": [ + {"name": "id", "type": "ReturnRequestId"}, + {"name": "orderId", "type": "OrderId"}, + {"name": "reason", "type": "String"}, + {"name": "refundAmount", "type": "Money"}, + {"name": "status", "type": "ReturnStatus"} + ], + "behaviours": [ + { + "name": "submit", + "description": "Creates a new return request for a paid order", + "input": ["bb-3", "bb-4"], + "output": ["bb-5"], + "rules": ["rule-1"] + }, + { + "name": "approve", + "description": "Approves the return request, initiating refund", + "input": [], + "output": [], + "rules": ["rule-2", "rule-3"] + } + ] + }, + { + "id": "bb-2", + "name": "ReturnRequestId", + "type": "value_object", + "description": "Unique identifier for a return request", + "properties": [{"name": "value", "type": "UUID"}], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "OrderId", + "type": "value_object", + "description": "Unique identifier for an order.", + "properties": [{"name": "value", "type": "UUID"}], + "behaviours": [] + }, + { + "id": "bb-4", + "name": "Money", + "type": "value_object", + "description": "Monetary amount.", + "properties": [{"name": "amount", "type": "BigDecimal"}, {"name": "currency", "type": "String"}], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "ReturnRequested", + "type": "domain_event", + "description": "Emitted when a return request is submitted", + "properties": [ + {"name": "returnRequestId", "type": "ReturnRequestId"}, + {"name": "orderId", "type": "OrderId"}, + {"name": "refundAmount", "type": "Money"} + ], + "behaviours": [] + }, + { + "id": "bb-6", + "name": "ReturnReceipt", + "type": "aggregate", + "description": "Tracks the physical receipt of returned goods at the warehouse", + "properties": [ + {"name": "id", "type": "ReturnReceiptId"}, + {"name": "returnRequestId", "type": "ReturnRequestId"}, + {"name": "receivedAt", "type": "Instant"}, + {"name": "inspected", "type": "boolean"} + ], + "behaviours": [ + { + "name": "receive", + "description": "Records that returned goods have been received", + "input": ["bb-2"], + "output": ["bb-8"], + "rules": [] + }, + { + "name": "inspect", + "description": "Marks the returned goods as inspected", + "input": [], + "output": [], + "rules": [] + } + ] + }, + { + "id": "bb-7", + "name": "ReturnReceiptId", + "type": "value_object", + "description": "Unique identifier for a return receipt", + "properties": [{"name": "value", "type": "UUID"}], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "ReturnReceived", + "type": "domain_event", + "description": "Emitted when returned goods are received at the warehouse", + "properties": [ + {"name": "returnReceiptId", "type": "ReturnReceiptId"}, + {"name": "returnRequestId", "type": "ReturnRequestId"}, + {"name": "receivedAt", "type": "Instant"} + ], + "behaviours": [] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Submit Return", + "actor": "actor-1", + "type": "Command", + "description": "Customer submits a return request for a paid order", + "businessGoal": "bg-1", + "input": ["bb-3", "bb-4"], + "output": ["bb-5"], + "usedBuildingBlocks": ["bb-1"], + "rules": ["rule-1", "rule-4"], + "scenarios": [], + "qualities": [] + } + ], + "scenarios": [] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-shipping.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-shipping.json new file mode 100644 index 0000000..03d1565 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-shipping.json @@ -0,0 +1,224 @@ +{ + "actors": [ + {"id": "actor-1", "name": "Warehouse Operator", "description": "Manages order shipments from the warehouse"} + ], + "businessGoals": [ + {"id": "bg-1", "name": "Reliable Shipping", "description": "Ensure orders are shipped correctly and on time"} + ], + "domainConcepts": [ + {"id": "dc-1", "name": "Shipment", "description": "A package sent to the customer containing ordered items"}, + {"id": "dc-2", "name": "Tracking Number", "description": "Unique identifier from the carrier for tracking a shipment"}, + {"id": "dc-3", "name": "Shipping Address", "description": "Destination where the shipment is delivered"} + ], + "rules": [ + {"id": "rule-1", "ruleType": "Consistency", "description": "A shipment can only be created for a submitted order"}, + {"id": "rule-2", "ruleType": "State change", "description": "A shipment can only be dispatched if it has a tracking number assigned"}, + {"id": "rule-3", "ruleType": "Consistency", "description": "A delivered shipment cannot be dispatched again"}, + {"id": "rule-4", "ruleType": "Structure", "description": "Shipping address must have street, city, and postal code"} + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Orders", + "description": "Order management bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Shipping", + "description": "Handles shipment lifecycle for orders", + "buildingBlocks": ["bb-1", "bb-2", "bb-3", "bb-4", "bb-5", "bb-6", "bb-7", "bb-8"] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "Shipment", + "type": "aggregate", + "description": "Aggregate root managing the lifecycle of a shipment. Contains ShippingAddress value object.", + "properties": [ + {"name": "id", "type": "ShipmentId"}, + {"name": "orderId", "type": "OrderId"}, + {"name": "address", "type": "ShippingAddress"}, + {"name": "trackingNumber", "type": "TrackingNumber"}, + {"name": "status", "type": "ShipmentStatus"} + ], + "behaviours": [ + { + "name": "createForOrder", + "description": "Creates a new shipment for a submitted order", + "input": ["bb-2", "bb-3"], + "output": ["bb-5"], + "rules": ["rule-1"] + }, + { + "name": "assignTrackingNumber", + "description": "Assigns a carrier tracking number to the shipment", + "input": ["bb-4"], + "output": [], + "rules": [] + }, + { + "name": "dispatch", + "description": "Marks the shipment as dispatched", + "input": [], + "output": ["bb-6"], + "rules": ["rule-2", "rule-3"] + }, + { + "name": "confirmDelivery", + "description": "Marks the shipment as delivered", + "input": [], + "output": [], + "rules": ["rule-3"] + } + ] + }, + { + "id": "bb-2", + "name": "ShipmentId", + "type": "value_object", + "description": "Unique identifier for a shipment", + "properties": [ + {"name": "value", "type": "UUID"} + ], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "ShippingAddress", + "type": "value_object", + "description": "Destination address for a shipment", + "properties": [ + {"name": "street", "type": "String"}, + {"name": "city", "type": "String"}, + {"name": "postalCode", "type": "String"}, + {"name": "country", "type": "String"} + ], + "behaviours": [], + "rules": ["rule-4"] + }, + { + "id": "bb-4", + "name": "TrackingNumber", + "type": "value_object", + "description": "Carrier-assigned tracking number", + "properties": [ + {"name": "value", "type": "String"} + ], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "ShipmentCreated", + "type": "domain_event", + "description": "Emitted when a new shipment is created for an order", + "properties": [ + {"name": "shipmentId", "type": "ShipmentId"}, + {"name": "orderId", "type": "OrderId"}, + {"name": "createdAt", "type": "Instant"} + ], + "behaviours": [] + }, + { + "id": "bb-6", + "name": "ShipmentDispatched", + "type": "domain_event", + "description": "Emitted when a shipment is dispatched from warehouse", + "properties": [ + {"name": "shipmentId", "type": "ShipmentId"}, + {"name": "trackingNumber", "type": "TrackingNumber"}, + {"name": "dispatchedAt", "type": "Instant"} + ], + "behaviours": [] + }, + { + "id": "bb-7", + "name": "ShipmentRepository", + "type": "repository", + "description": "Repository for persisting and retrieving Shipment aggregates", + "properties": [], + "behaviours": [ + { + "name": "save", + "description": "Persists a shipment", + "input": ["bb-1"], + "output": [], + "rules": [] + }, + { + "name": "findById", + "description": "Finds a shipment by its id", + "input": ["bb-2"], + "output": ["bb-1"], + "rules": [] + } + ] + }, + { + "id": "bb-8", + "name": "ShipmentStatus", + "type": "value_object", + "description": "Enum representing shipment lifecycle: Created, Dispatched, Delivered", + "properties": [ + {"name": "value", "type": "String"} + ], + "behaviours": [] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Create Shipment", + "actor": "actor-1", + "type": "Command", + "description": "Creates a shipment for a submitted order", + "businessGoal": "bg-1", + "input": ["bb-2", "bb-3"], + "output": ["bb-5"], + "usedBuildingBlocks": ["bb-1", "bb-7"], + "rules": ["rule-1"], + "scenarios": [ + { + "name": "Successful shipment creation", + "description": "Operator creates a shipment for a submitted order", + "given": "A submitted order exists with a valid shipping address", + "when": "Warehouse operator creates a shipment", + "then": "Shipment is created and ShipmentCreated event is emitted" + } + ], + "qualities": [] + }, + { + "id": "uc-2", + "name": "Dispatch Shipment", + "actor": "actor-1", + "type": "Command", + "description": "Dispatches a shipment with tracking number", + "input": ["bb-2"], + "output": ["bb-6"], + "usedBuildingBlocks": ["bb-1", "bb-7"], + "rules": ["rule-2", "rule-3"], + "scenarios": [ + { + "name": "Successful dispatch", + "description": "Shipment with tracking number is dispatched", + "given": "A shipment with an assigned tracking number", + "when": "Operator dispatches the shipment", + "then": "Shipment status changes to Dispatched and ShipmentDispatched event is emitted" + }, + { + "name": "Dispatch without tracking fails", + "description": "Cannot dispatch without tracking number", + "given": "A shipment without a tracking number", + "when": "Operator tries to dispatch", + "then": "Dispatch is rejected" + } + ], + "qualities": [] + } + ], + "scenarios": [] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-transfers.json b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-transfers.json new file mode 100644 index 0000000..6579b3c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/designdoc-transfers.json @@ -0,0 +1,324 @@ +{ + "actors": [ + { + "id": "actor-1", + "name": "Account Holder", + "description": "Bank customer who initiates transfers" + } + ], + "businessGoals": [ + { + "id": "bg-1", + "name": "Reliable Transfers", + "description": "Execute money transfers between accounts safely and atomically" + } + ], + "domainConcepts": [ + { + "id": "dc-1", + "name": "Transfer", + "description": "Movement of funds from one account to another" + }, + { + "id": "dc-2", + "name": "Transfer Limit", + "description": "Maximum amount that can be transferred in a single transaction" + } + ], + "rules": [ + { + "id": "rule-1", + "ruleType": "Consistency", + "description": "Source account must have sufficient funds for the transfer" + }, + { + "id": "rule-2", + "ruleType": "Consistency", + "description": "Source and destination accounts must be different" + }, + { + "id": "rule-3", + "ruleType": "Structure", + "description": "Transfer amount must be positive" + }, + { + "id": "rule-4", + "ruleType": "Computation", + "description": "Daily transfer limit is the sum of all transfers made by the source account on the current day" + }, + { + "id": "rule-5", + "ruleType": "State change", + "description": "A completed transfer cannot be modified" + } + ], + "qualityAttributes": [], + "boundedContexts": [ + { + "id": "bc-1", + "name": "Banking", + "description": "Core banking bounded context", + "modules": [ + { + "id": "mod-1", + "name": "Transfers", + "description": "Handles money transfers between accounts", + "buildingBlocks": [ + "bb-1", + "bb-2", + "bb-3", + "bb-4", + "bb-5", + "bb-6", + "bb-7", + "bb-8" + ] + } + ] + } + ], + "buildingBlocks": [ + { + "id": "bb-1", + "name": "Transfer", + "type": "aggregate", + "description": "Aggregate root representing a money transfer between two accounts", + "properties": [ + { + "name": "id", + "type": "TransferId" + }, + { + "name": "sourceAccountId", + "type": "AccountId" + }, + { + "name": "destinationAccountId", + "type": "AccountId" + }, + { + "name": "amount", + "type": "Amount" + }, + { + "name": "status", + "type": "TransferStatus" + }, + { + "name": "initiatedAt", + "type": "Instant" + } + ], + "behaviours": [ + { + "name": "initiate", + "description": "Initiates a new transfer between two accounts", + "input": [ + "bb-3", + "bb-3", + "bb-4" + ], + "output": [ + "bb-6" + ], + "rules": [ + "rule-2", + "rule-3" + ] + }, + { + "name": "complete", + "description": "Marks the transfer as completed after funds are moved", + "input": [], + "output": [ + "bb-7" + ], + "rules": [ + "rule-5" + ] + }, + { + "name": "fail", + "description": "Marks the transfer as failed", + "input": [], + "output": [], + "rules": [ + "rule-5" + ] + } + ] + }, + { + "id": "bb-2", + "name": "TransferId", + "type": "value_object", + "description": "Unique identifier for a transfer", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-3", + "name": "AccountId", + "type": "value_object", + "description": "Unique identifier for an account.", + "properties": [ + { + "name": "value", + "type": "UUID" + } + ], + "behaviours": [] + }, + { + "id": "bb-4", + "name": "Amount", + "type": "value_object", + "description": "Monetary amount.", + "properties": [ + { + "name": "value", + "type": "BigDecimal" + } + ], + "behaviours": [] + }, + { + "id": "bb-5", + "name": "TransferStatus", + "type": "value_object", + "description": "Enum representing transfer lifecycle: Initiated, Completed, Failed", + "properties": [ + { + "name": "value", + "type": "String" + } + ], + "behaviours": [] + }, + { + "id": "bb-6", + "name": "TransferInitiated", + "type": "domain_event", + "description": "Emitted when a transfer is initiated", + "properties": [ + { + "name": "transferId", + "type": "TransferId" + }, + { + "name": "sourceAccountId", + "type": "AccountId" + }, + { + "name": "destinationAccountId", + "type": "AccountId" + }, + { + "name": "amount", + "type": "Amount" + }, + { + "name": "initiatedAt", + "type": "Instant" + } + ], + "behaviours": [] + }, + { + "id": "bb-7", + "name": "TransferCompleted", + "type": "domain_event", + "description": "Emitted when a transfer is completed successfully", + "properties": [ + { + "name": "transferId", + "type": "TransferId" + }, + { + "name": "completedAt", + "type": "Instant" + } + ], + "behaviours": [] + }, + { + "id": "bb-8", + "name": "TransferRepository", + "type": "repository", + "description": "Repository for persisting and retrieving Transfer aggregates", + "properties": [], + "behaviours": [ + { + "name": "save", + "description": "Persists a transfer", + "input": [ + "bb-1" + ], + "output": [], + "rules": [] + }, + { + "name": "findById", + "description": "Finds a transfer by its id", + "input": [ + "bb-2" + ], + "output": [ + "bb-1" + ], + "rules": [] + } + ] + } + ], + "useCases": [ + { + "id": "uc-1", + "name": "Initiate Transfer", + "actor": "actor-1", + "type": "Command", + "description": "Account holder initiates a transfer from their account to another", + "businessGoal": "bg-1", + "input": [ + "bb-3", + "bb-3", + "bb-4" + ], + "output": [ + "bb-6" + ], + "usedBuildingBlocks": [ + "bb-1", + "bb-8" + ], + "rules": [ + "rule-1", + "rule-2", + "rule-3" + ], + "scenarios": [ + { + "name": "Successful transfer initiation", + "description": "Transfer between two different accounts with sufficient funds", + "given": "Source account with balance 1000 and destination account", + "when": "Account holder initiates transfer of 500", + "then": "Transfer is created with Initiated status and TransferInitiated event is emitted" + }, + { + "name": "Transfer to same account rejected", + "description": "Cannot transfer to the same account", + "given": "An account", + "when": "Account holder tries to transfer to the same account", + "then": "Transfer is rejected" + } + ], + "qualities": [] + } + ], + "scenarios": [] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/pom.xml new file mode 100644 index 0000000..d330024 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + net.ecom + ecommerce + 1.0-SNAPSHOT + + 17 + 17 + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/fulfillment/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/fulfillment/Shipment.java new file mode 100644 index 0000000..862968e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/fulfillment/Shipment.java @@ -0,0 +1,52 @@ +package net.ecom.fulfillment; + +import net.ecom.sales.shared.OrderId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public final class Shipment { + + public sealed interface Event permits ShipmentCreated, ShipmentDispatched {} + public record ShipmentCreated(UUID shipmentId, OrderId orderId, Instant createdAt) implements Event {} + public record ShipmentDispatched(UUID shipmentId, Instant dispatchedAt) implements Event {} + + private final UUID id; + private final OrderId orderId; + private boolean dispatched = false; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(UUID id, OrderId orderId) { + this.id = id; + this.orderId = orderId; + } + + public static Shipment createFor(OrderId orderId) { + var id = UUID.randomUUID(); + var shipment = new Shipment(id, orderId); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void dispatch() { + if (dispatched) throw new IllegalStateException("Already dispatched"); + this.dispatched = true; + pendingEvents.add(new ShipmentDispatched(id, Instant.now())); + } + + public UUID id() { return id; } + public OrderId orderId() { return orderId; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(Shipment shipment); + Shipment findByOrderId(OrderId orderId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/Order.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/Order.java new file mode 100644 index 0000000..b16d201 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/Order.java @@ -0,0 +1,67 @@ +package net.ecom.sales.orders; + +import net.ecom.sales.shared.CustomerId; +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Order { + + public sealed interface Event permits OrderPlaced, OrderPaid {} + public record OrderPlaced(OrderId orderId, CustomerId customerId, Money total, Instant placedAt) implements Event {} + public record OrderPaid(OrderId orderId, Instant paidAt) implements Event {} + + enum Status { DRAFT, PLACED, PAID } + + private final OrderId id; + private final CustomerId customerId; + private final List items = new ArrayList<>(); + private Status status = Status.DRAFT; + private Money total = Money.zero("USD"); + private final List pendingEvents = new ArrayList<>(); + + private Order(OrderId id, CustomerId customerId) { + this.id = id; + this.customerId = customerId; + } + + public static Order create(CustomerId customerId) { + return new Order(OrderId.generate(), customerId); + } + + public void addItem(String item, Money price) { + items.add(item); + total = total.add(price); + } + + public void place() { + if (items.isEmpty()) throw new IllegalStateException("Cannot place empty order"); + this.status = Status.PLACED; + pendingEvents.add(new OrderPlaced(id, customerId, total, Instant.now())); + } + + public void markPaid() { + if (status != Status.PLACED) throw new IllegalStateException("Only placed orders can be paid"); + this.status = Status.PAID; + pendingEvents.add(new OrderPaid(id, Instant.now())); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public Money total() { return total; } + public boolean isPaid() { return status == Status.PAID; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(Order order); + Order findById(OrderId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/PlaceOrderHandler.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/PlaceOrderHandler.java new file mode 100644 index 0000000..dc06621 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/orders/PlaceOrderHandler.java @@ -0,0 +1,20 @@ +package net.ecom.sales.orders; + +import net.ecom.sales.shared.CustomerId; +import net.ecom.sales.shared.Money; + +public final class PlaceOrderHandler { + + private final Order.Repository repository; + + public PlaceOrderHandler(Order.Repository repository) { + this.repository = repository; + } + + public void handle(CustomerId customerId, String item, Money price) { + var order = Order.create(customerId); + order.addItem(item, price); + order.place(); + repository.save(order); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/CustomerId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/CustomerId.java new file mode 100644 index 0000000..ee45b7a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/CustomerId.java @@ -0,0 +1,8 @@ +package net.ecom.sales.shared; + +import java.util.Objects; +import java.util.UUID; + +public record CustomerId(UUID value) { + public CustomerId { Objects.requireNonNull(value); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/Money.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/Money.java new file mode 100644 index 0000000..ccb0d89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/Money.java @@ -0,0 +1,9 @@ +package net.ecom.sales.shared; + +import java.math.BigDecimal; + +public record Money(BigDecimal amount, String currency) { + public static Money of(BigDecimal amount, String currency) { return new Money(amount, currency); } + public static Money zero(String currency) { return new Money(BigDecimal.ZERO, currency); } + public Money add(Money other) { return new Money(amount.add(other.amount), currency); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/OrderId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/OrderId.java new file mode 100644 index 0000000..1c67045 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-cross-module/src/main/java/net/ecom/sales/shared/OrderId.java @@ -0,0 +1,9 @@ +package net.ecom.sales.shared; + +import java.util.Objects; +import java.util.UUID; + +public record OrderId(UUID value) { + public OrderId { Objects.requireNonNull(value); } + public static OrderId generate() { return new OrderId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/pom.xml new file mode 100644 index 0000000..a7feaa5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + org.store + order-service + 1.0-SNAPSHOT + + 17 + 17 + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/application/PlaceOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/application/PlaceOrderService.java new file mode 100644 index 0000000..dc6b447 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/application/PlaceOrderService.java @@ -0,0 +1,21 @@ +package org.store.orders.application; + +import org.store.orders.domain.Order; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class PlaceOrderService { + + private final OrderRepository orderRepository; + + public PlaceOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public void place(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + order.place(); + orderRepository.save(order); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/CustomerId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/CustomerId.java new file mode 100644 index 0000000..d5170b3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/CustomerId.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record CustomerId(UUID value) { + public CustomerId { + Objects.requireNonNull(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Money.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Money.java new file mode 100644 index 0000000..cc8beab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Money.java @@ -0,0 +1,23 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; + +public record Money(BigDecimal amount, String currency) { + public Money { + if (amount == null) throw new IllegalArgumentException("Amount cannot be null"); + if (currency == null || currency.isBlank()) throw new IllegalArgumentException("Currency required"); + } + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + public Money add(Money other) { + if (!currency.equals(other.currency)) throw new IllegalArgumentException("Currency mismatch"); + return new Money(amount.add(other.amount), currency); + } + public boolean isPositive() { + return amount.compareTo(BigDecimal.ZERO) > 0; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Order.java new file mode 100644 index 0000000..bb5fe49 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/Order.java @@ -0,0 +1,55 @@ +package org.store.orders.domain; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class Order { + + enum Status { DRAFT, PLACED } + + private final OrderId id; + private final CustomerId customerId; + private final List lines; + private Status status; + private final Instant createdAt; + + private Order(OrderId id, CustomerId customerId, Instant createdAt) { + this.id = id; + this.customerId = customerId; + this.lines = new ArrayList<>(); + this.status = Status.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(CustomerId customerId, Instant now) { + return new Order(OrderId.generate(), customerId, now); + } + + public void addLine(String productName, int quantity, Money unitPrice) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productName, quantity, unitPrice)); + } + + public void place() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot place an empty order"); + } + this.status = Status.PLACED; + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public List lines() { return Collections.unmodifiableList(lines); } + public boolean isDraft() { return status == Status.DRAFT; } + public boolean isPlaced() { return status == Status.PLACED; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderId.java new file mode 100644 index 0000000..95b4383 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderId.java @@ -0,0 +1,13 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record OrderId(UUID value) { + public OrderId { + Objects.requireNonNull(value); + } + public static OrderId generate() { + return new OrderId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderLine.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderLine.java new file mode 100644 index 0000000..d2bd921 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderLine.java @@ -0,0 +1,12 @@ +package org.store.orders.domain; + +record OrderLine(String productName, int quantity, Money unitPrice) { + + OrderLine { + if (quantity <= 0) throw new IllegalArgumentException("Quantity must be positive"); + } + + Money lineTotal() { + return Money.of(unitPrice.amount().multiply(java.math.BigDecimal.valueOf(quantity)), unitPrice.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderRepository.java new file mode 100644 index 0000000..1f11aa9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-delta-existing/src/main/java/org/store/orders/domain/OrderRepository.java @@ -0,0 +1,8 @@ +package org.store.orders.domain; + +import java.util.Optional; + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/pom.xml new file mode 100644 index 0000000..4f33e2e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.0 + + com.example + order-management + 1.0-SNAPSHOT + + 17 + + + + org.springframework.boot + spring-boot-starter + + + org.projectlombok + lombok + provided + + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/CreateOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/CreateOrderService.java new file mode 100644 index 0000000..3b26458 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/CreateOrderService.java @@ -0,0 +1,30 @@ +package com.example.orders.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.Order; +import com.example.orders.domain.OrderId; +import com.example.orders.domain.OrderRepository; + +import java.time.Clock; +import java.time.Instant; + +class CreateOrderService { + + private final OrderRepository orderRepository; + private final DomainEventPublisher eventPublisher; + private final Clock clock; + + CreateOrderService(OrderRepository orderRepository, DomainEventPublisher eventPublisher, Clock clock) { + this.orderRepository = orderRepository; + this.eventPublisher = eventPublisher; + this.clock = clock; + } + + OrderId create() { + OrderId orderId = OrderId.generate(); + Order order = Order.create(orderId, Instant.now(clock)); + orderRepository.save(order); + order.flushEvents().forEach(eventPublisher::publish); + return orderId; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderConfiguration.java new file mode 100644 index 0000000..8392d94 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderConfiguration.java @@ -0,0 +1,32 @@ +package com.example.orders.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Clock; + +@Configuration +class OrderConfiguration { + + @Bean + OrderFacade orderFacade(CreateOrderService createOrderService, + SubmitOrderService submitOrderService, + OrderRepository orderRepository) { + return new OrderFacade(createOrderService, submitOrderService, orderRepository); + } + + @Bean + CreateOrderService createOrderService(OrderRepository orderRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + return new CreateOrderService(orderRepository, eventPublisher, clock); + } + + @Bean + SubmitOrderService submitOrderService(OrderRepository orderRepository, + DomainEventPublisher eventPublisher) { + return new SubmitOrderService(orderRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderFacade.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderFacade.java new file mode 100644 index 0000000..f455bd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/OrderFacade.java @@ -0,0 +1,34 @@ +package com.example.orders.application; + +import com.example.orders.domain.Order; +import com.example.orders.domain.OrderId; +import com.example.orders.domain.OrderRepository; + +import java.util.Optional; + +public class OrderFacade { + + private final CreateOrderService createOrderService; + private final SubmitOrderService submitOrderService; + private final OrderRepository orderRepository; + + OrderFacade(CreateOrderService createOrderService, + SubmitOrderService submitOrderService, + OrderRepository orderRepository) { + this.createOrderService = createOrderService; + this.submitOrderService = submitOrderService; + this.orderRepository = orderRepository; + } + + public OrderId createOrder() { + return createOrderService.create(); + } + + public void submitOrder(OrderId orderId) { + submitOrderService.submit(orderId); + } + + public Optional findOrder(OrderId orderId) { + return orderRepository.findById(orderId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/SubmitOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/SubmitOrderService.java new file mode 100644 index 0000000..e14b10b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/application/SubmitOrderService.java @@ -0,0 +1,25 @@ +package com.example.orders.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.Order; +import com.example.orders.domain.OrderId; +import com.example.orders.domain.OrderRepository; + +class SubmitOrderService { + + private final OrderRepository orderRepository; + private final DomainEventPublisher eventPublisher; + + SubmitOrderService(OrderRepository orderRepository, DomainEventPublisher eventPublisher) { + this.orderRepository = orderRepository; + this.eventPublisher = eventPublisher; + } + + void submit(OrderId orderId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found: " + orderId)); + order.submit(); + orderRepository.save(order); + order.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEvent.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEvent.java new file mode 100644 index 0000000..ab06a41 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEvent.java @@ -0,0 +1,4 @@ +package com.example.orders.domain; + +public interface DomainEvent { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEventPublisher.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEventPublisher.java new file mode 100644 index 0000000..a9cca5e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/DomainEventPublisher.java @@ -0,0 +1,5 @@ +package com.example.orders.domain; + +public interface DomainEventPublisher { + void publish(DomainEvent event); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Money.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Money.java new file mode 100644 index 0000000..a5505df --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Money.java @@ -0,0 +1,38 @@ +package com.example.orders.domain; + +import lombok.Value; + +import java.math.BigDecimal; + +@Value +public class Money { + BigDecimal amount; + String currency; + + public static Money of(BigDecimal amount, String currency) { + if (amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + return new Money(amount, currency); + } + + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + + public Money add(Money other) { + assertSameCurrency(other); + return new Money(amount.add(other.amount), currency); + } + + public Money subtract(Money other) { + assertSameCurrency(other); + return new Money(amount.subtract(other.amount), currency); + } + + private void assertSameCurrency(Money other) { + if (!currency.equals(other.currency)) { + throw new IllegalArgumentException("Currency mismatch"); + } + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Order.java new file mode 100644 index 0000000..a112dc6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/Order.java @@ -0,0 +1,58 @@ +package com.example.orders.domain; + +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Getter +public class Order { + + private final OrderId id; + private final List lines; + private OrderStatus status; + private final Instant createdAt; + private final List pendingEvents = new ArrayList<>(); + + private Order(OrderId id, Instant createdAt) { + this.id = id; + this.lines = new ArrayList<>(); + this.status = OrderStatus.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(OrderId id, Instant createdAt) { + Order order = new Order(id, createdAt); + order.pendingEvents.add(new OrderCreated(id, createdAt)); + return order; + } + + public void addLine(ProductId productId, int quantity, Money unitPrice) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productId, quantity, unitPrice)); + } + + public void submit() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot submit empty order"); + } + this.status = OrderStatus.SUBMITTED; + pendingEvents.add(new OrderSubmitted(id, total(), Instant.now())); + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderCreated.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderCreated.java new file mode 100644 index 0000000..ff56239 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderCreated.java @@ -0,0 +1,11 @@ +package com.example.orders.domain; + +import lombok.Value; + +import java.time.Instant; + +@Value +public class OrderCreated implements DomainEvent { + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderId.java new file mode 100644 index 0000000..7922df2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderId.java @@ -0,0 +1,18 @@ +package com.example.orders.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class OrderId { + UUID value; + + public static OrderId generate() { + return new OrderId(UUID.randomUUID()); + } + + public static OrderId of(UUID value) { + return new OrderId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderLine.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderLine.java new file mode 100644 index 0000000..66a5c26 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderLine.java @@ -0,0 +1,14 @@ +package com.example.orders.domain; + +import lombok.Value; + +@Value +public class OrderLine { + ProductId productId; + int quantity; + Money unitPrice; + + public Money lineTotal() { + return Money.of(unitPrice.getAmount().multiply(java.math.BigDecimal.valueOf(quantity)), unitPrice.getCurrency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderRepository.java new file mode 100644 index 0000000..17097dc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.domain; + +import java.util.Optional; + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderStatus.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderStatus.java new file mode 100644 index 0000000..da0f2e1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.domain; + +public enum OrderStatus { + DRAFT, SUBMITTED, CONFIRMED, SHIPPED, CANCELLED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderSubmitted.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderSubmitted.java new file mode 100644 index 0000000..e9b27ab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/OrderSubmitted.java @@ -0,0 +1,12 @@ +package com.example.orders.domain; + +import lombok.Value; + +import java.time.Instant; + +@Value +public class OrderSubmitted implements DomainEvent { + OrderId orderId; + Money total; + Instant submittedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/ProductId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/ProductId.java new file mode 100644 index 0000000..63c821b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/domain/ProductId.java @@ -0,0 +1,14 @@ +package com.example.orders.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ProductId { + UUID value; + + public static ProductId of(UUID value) { + return new ProductId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentConfiguration.java new file mode 100644 index 0000000..81cbbe0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentConfiguration.java @@ -0,0 +1,22 @@ +package com.example.orders.fulfillment.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.fulfillment.domain.FulfillmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class FulfillmentConfiguration { + + @Bean + FulfillmentFacade fulfillmentFacade(StartFulfillmentService startFulfillmentService, + FulfillmentRepository fulfillmentRepository) { + return new FulfillmentFacade(startFulfillmentService, fulfillmentRepository); + } + + @Bean + StartFulfillmentService startFulfillmentService(FulfillmentRepository fulfillmentRepository, + DomainEventPublisher eventPublisher) { + return new StartFulfillmentService(fulfillmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentFacade.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentFacade.java new file mode 100644 index 0000000..28e0905 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/FulfillmentFacade.java @@ -0,0 +1,28 @@ +package com.example.orders.fulfillment.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.fulfillment.domain.Fulfillment; +import com.example.orders.fulfillment.domain.FulfillmentId; +import com.example.orders.fulfillment.domain.FulfillmentRepository; + +import java.util.Optional; + +public class FulfillmentFacade { + + private final StartFulfillmentService startFulfillmentService; + private final FulfillmentRepository fulfillmentRepository; + + FulfillmentFacade(StartFulfillmentService startFulfillmentService, + FulfillmentRepository fulfillmentRepository) { + this.startFulfillmentService = startFulfillmentService; + this.fulfillmentRepository = fulfillmentRepository; + } + + public FulfillmentId startFulfillment(OrderId orderId) { + return startFulfillmentService.start(orderId); + } + + public Optional findFulfillment(FulfillmentId id) { + return fulfillmentRepository.findById(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/StartFulfillmentService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/StartFulfillmentService.java new file mode 100644 index 0000000..047257f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/application/StartFulfillmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.fulfillment.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.fulfillment.domain.Fulfillment; +import com.example.orders.fulfillment.domain.FulfillmentId; +import com.example.orders.fulfillment.domain.FulfillmentRepository; + +class StartFulfillmentService { + + private final FulfillmentRepository fulfillmentRepository; + private final DomainEventPublisher eventPublisher; + + StartFulfillmentService(FulfillmentRepository fulfillmentRepository, + DomainEventPublisher eventPublisher) { + this.fulfillmentRepository = fulfillmentRepository; + this.eventPublisher = eventPublisher; + } + + FulfillmentId start(OrderId orderId) { + Fulfillment fulfillment = Fulfillment.startFor(orderId); + fulfillmentRepository.save(fulfillment); + fulfillment.flushEvents().forEach(eventPublisher::publish); + return fulfillment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/Fulfillment.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/Fulfillment.java new file mode 100644 index 0000000..56936bd --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/Fulfillment.java @@ -0,0 +1,44 @@ +package com.example.orders.fulfillment.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Fulfillment { + + private final FulfillmentId id; + private final OrderId orderId; + private FulfillmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Fulfillment(FulfillmentId id, OrderId orderId) { + this.id = id; + this.orderId = orderId; + this.status = FulfillmentStatus.PENDING; + } + + public static Fulfillment startFor(OrderId orderId) { + FulfillmentId id = FulfillmentId.generate(); + Fulfillment fulfillment = new Fulfillment(id, orderId); + fulfillment.pendingEvents.add(new FulfillmentStarted(id, orderId, Instant.now())); + return fulfillment; + } + + public void markReady() { + if (status != FulfillmentStatus.PENDING) { + throw new IllegalStateException("Only pending fulfillments can be marked ready"); + } + this.status = FulfillmentStatus.READY; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentId.java new file mode 100644 index 0000000..423dff3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentId.java @@ -0,0 +1,14 @@ +package com.example.orders.fulfillment.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class FulfillmentId { + UUID value; + + public static FulfillmentId generate() { + return new FulfillmentId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentRepository.java new file mode 100644 index 0000000..75d8edb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentRepository.java @@ -0,0 +1,11 @@ +package com.example.orders.fulfillment.domain; + +import com.example.orders.domain.OrderId; + +import java.util.Optional; + +public interface FulfillmentRepository { + void save(Fulfillment fulfillment); + Optional findById(FulfillmentId id); + Optional findByOrderId(OrderId orderId); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStarted.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStarted.java new file mode 100644 index 0000000..43bd210 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStarted.java @@ -0,0 +1,14 @@ +package com.example.orders.fulfillment.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class FulfillmentStarted implements DomainEvent { + FulfillmentId fulfillmentId; + OrderId orderId; + Instant startedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStatus.java new file mode 100644 index 0000000..f092130 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-lombok-spring/src/main/java/com/example/orders/fulfillment/domain/FulfillmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.fulfillment.domain; + +public enum FulfillmentStatus { + PENDING, READY, SHIPPED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/pom.xml new file mode 100644 index 0000000..7546c8b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + com.acme + hr-system + 1.0-SNAPSHOT + + 17 + 17 + + + + io.vavr + vavr + 0.10.4 + + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/Employee.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/Employee.java new file mode 100644 index 0000000..4c3ab17 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/Employee.java @@ -0,0 +1,30 @@ +package com.acme.hr.employees; + +public class Employee { + + private EmployeeId id; + private String firstName; + private String lastName; + private String department; + + public Employee() {} + + public Employee(EmployeeId id, String firstName, String lastName, String department) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.department = department; + } + + public EmployeeId getId() { return id; } + public void setId(EmployeeId id) { this.id = id; } + + public String getFirstName() { return firstName; } + public void setFirstName(String firstName) { this.firstName = firstName; } + + public String getLastName() { return lastName; } + public void setLastName(String lastName) { this.lastName = lastName; } + + public String getDepartment() { return department; } + public void setDepartment(String department) { this.department = department; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeId.java new file mode 100644 index 0000000..6855923 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeId.java @@ -0,0 +1,26 @@ +package com.acme.hr.employees; + +import java.util.UUID; + +public class EmployeeId { + + private UUID value; + + public EmployeeId() {} + + public EmployeeId(UUID value) { + this.value = value; + } + + public UUID getValue() { + return value; + } + + public void setValue(UUID value) { + this.value = value; + } + + public static EmployeeId generate() { + return new EmployeeId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeRepository.java new file mode 100644 index 0000000..cc46a2d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeRepository.java @@ -0,0 +1,6 @@ +package com.acme.hr.employees; + +public interface EmployeeRepository { + Employee findById(EmployeeId id); + void save(Employee employee); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeService.java new file mode 100644 index 0000000..04def0c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/employees/EmployeeService.java @@ -0,0 +1,25 @@ +package com.acme.hr.employees; + +public class EmployeeService { + + private final EmployeeRepository employeeRepository; + + public EmployeeService(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + public void changeDepartment(EmployeeId employeeId, String newDepartment) { + Employee employee = employeeRepository.findById(employeeId); + if (employee == null) { + throw new RuntimeException("Employee not found"); + } + employee.setDepartment(newDepartment); + employeeRepository.save(employee); + } + + public Employee hire(String firstName, String lastName, String department) { + Employee employee = new Employee(EmployeeId.generate(), firstName, lastName, department); + employeeRepository.save(employee); + return employee; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/ApproveLeaveService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/ApproveLeaveService.java new file mode 100644 index 0000000..065fa07 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/ApproveLeaveService.java @@ -0,0 +1,24 @@ +package com.acme.hr.leave.application; + +import com.acme.hr.leave.domain.LeaveApproved; +import com.acme.hr.leave.domain.LeaveRequest; +import com.acme.hr.leave.domain.LeaveRequestId; +import com.acme.hr.leave.domain.LeaveRequestRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ApproveLeaveService { + + private final LeaveRequestRepository leaveRequestRepository; + + public ApproveLeaveService(LeaveRequestRepository leaveRequestRepository) { + this.leaveRequestRepository = leaveRequestRepository; + } + + public Either approve(LeaveRequestId requestId) { + return leaveRequestRepository.findById(requestId) + .map(LeaveRequest::approve) + .orElse(left("Leave request not found")); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/SubmitLeaveService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/SubmitLeaveService.java new file mode 100644 index 0000000..ed83295 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/application/SubmitLeaveService.java @@ -0,0 +1,20 @@ +package com.acme.hr.leave.application; + +import com.acme.hr.employees.EmployeeId; +import com.acme.hr.leave.domain.DateRange; +import com.acme.hr.leave.domain.LeaveRequest; +import com.acme.hr.leave.domain.LeaveRequestRepository; + +public class SubmitLeaveService { + + private final LeaveRequestRepository leaveRequestRepository; + + public SubmitLeaveService(LeaveRequestRepository leaveRequestRepository) { + this.leaveRequestRepository = leaveRequestRepository; + } + + public void submit(EmployeeId employeeId, DateRange period) { + LeaveRequest request = LeaveRequest.submit(employeeId, period); + leaveRequestRepository.save(request); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/DateRange.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/DateRange.java new file mode 100644 index 0000000..1690aee --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/DateRange.java @@ -0,0 +1,46 @@ +package com.acme.hr.leave.domain; + +import java.time.LocalDate; +import java.util.Objects; + +public final class DateRange { + + private final LocalDate from; + private final LocalDate to; + + private DateRange(LocalDate from, LocalDate to) { + if (from.isAfter(to)) { + throw new IllegalArgumentException("Start date must be before end date"); + } + this.from = from; + this.to = to; + } + + public static DateRange of(LocalDate from, LocalDate to) { + return new DateRange(from, to); + } + + public int days() { + return (int) (to.toEpochDay() - from.toEpochDay()) + 1; + } + + public boolean overlaps(DateRange other) { + return !this.from.isAfter(other.to) && !this.to.isBefore(other.from); + } + + public LocalDate from() { return from; } + public LocalDate to() { return to; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DateRange dateRange = (DateRange) o; + return Objects.equals(from, dateRange.from) && Objects.equals(to, dateRange.to); + } + + @Override + public int hashCode() { + return Objects.hash(from, to); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveApproved.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveApproved.java new file mode 100644 index 0000000..e5968d9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveApproved.java @@ -0,0 +1,35 @@ +package com.acme.hr.leave.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.Objects; + +public final class LeaveApproved { + + private final LeaveRequestId requestId; + private final EmployeeId employeeId; + private final DateRange period; + + LeaveApproved(LeaveRequestId requestId, EmployeeId employeeId, DateRange period) { + this.requestId = requestId; + this.employeeId = employeeId; + this.period = period; + } + + public LeaveRequestId requestId() { return requestId; } + public EmployeeId employeeId() { return employeeId; } + public DateRange period() { return period; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LeaveApproved that = (LeaveApproved) o; + return Objects.equals(requestId, that.requestId); + } + + @Override + public int hashCode() { + return Objects.hash(requestId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRejected.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRejected.java new file mode 100644 index 0000000..21f94e5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRejected.java @@ -0,0 +1,30 @@ +package com.acme.hr.leave.domain; + +import java.util.Objects; + +public final class LeaveRejected { + + private final LeaveRequestId requestId; + private final String reason; + + LeaveRejected(LeaveRequestId requestId, String reason) { + this.requestId = requestId; + this.reason = reason; + } + + public LeaveRequestId requestId() { return requestId; } + public String reason() { return reason; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LeaveRejected that = (LeaveRejected) o; + return Objects.equals(requestId, that.requestId); + } + + @Override + public int hashCode() { + return Objects.hash(requestId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequest.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequest.java new file mode 100644 index 0000000..b003971 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequest.java @@ -0,0 +1,64 @@ +package com.acme.hr.leave.domain; + +import com.acme.hr.employees.EmployeeId; +import io.vavr.control.Either; + +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class LeaveRequest { + + enum Status { PENDING, APPROVED, REJECTED } + + private final LeaveRequestId id; + private final EmployeeId employeeId; + private final DateRange period; + private Status status; + + private LeaveRequest(LeaveRequestId id, EmployeeId employeeId, DateRange period) { + this.id = id; + this.employeeId = employeeId; + this.period = period; + this.status = Status.PENDING; + } + + public static LeaveRequest submit(EmployeeId employeeId, DateRange period) { + return new LeaveRequest(LeaveRequestId.generate(), employeeId, period); + } + + public Either approve() { + if (status != Status.PENDING) { + return left("Only pending requests can be approved"); + } + this.status = Status.APPROVED; + return right(new LeaveApproved(id, employeeId, period)); + } + + public Either reject(String reason) { + if (status != Status.PENDING) { + return left("Only pending requests can be rejected"); + } + this.status = Status.REJECTED; + return right(new LeaveRejected(id, reason)); + } + + public LeaveRequestId id() { return id; } + public EmployeeId employeeId() { return employeeId; } + public DateRange period() { return period; } + public boolean isPending() { return status == Status.PENDING; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LeaveRequest that = (LeaveRequest) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestId.java new file mode 100644 index 0000000..ee7af6a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestId.java @@ -0,0 +1,39 @@ +package com.acme.hr.leave.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class LeaveRequestId { + + private final UUID value; + + private LeaveRequestId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static LeaveRequestId of(UUID value) { + return new LeaveRequestId(value); + } + + public static LeaveRequestId generate() { + return new LeaveRequestId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LeaveRequestId that = (LeaveRequestId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestRepository.java new file mode 100644 index 0000000..d9fb0ba --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-mixed-patterns/src/main/java/com/acme/hr/leave/domain/LeaveRequestRepository.java @@ -0,0 +1,12 @@ +package com.acme.hr.leave.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.List; +import java.util.Optional; + +public interface LeaveRequestRepository { + void save(LeaveRequest leaveRequest); + Optional findById(LeaveRequestId id); + List findByEmployee(EmployeeId employeeId); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/pom.xml new file mode 100644 index 0000000..fe558b6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + pl.shop + catalog-service + 1.0-SNAPSHOT + + 11 + 11 + + + + io.vavr + vavr + 0.10.4 + + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Money.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Money.java new file mode 100644 index 0000000..c754a56 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Money.java @@ -0,0 +1,70 @@ +package pl.shop.catalog; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + if (amount == null) { + throw new IllegalArgumentException("Amount cannot be null"); + } + if (currency == null || currency.isBlank()) { + throw new IllegalArgumentException("Currency cannot be empty"); + } + this.amount = amount; + this.currency = currency; + } + + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + + public Money add(Money other) { + assertSameCurrency(other); + return new Money(amount.add(other.amount), currency); + } + + public Money subtract(Money other) { + assertSameCurrency(other); + return new Money(amount.subtract(other.amount), currency); + } + + public boolean isPositive() { + return amount.compareTo(BigDecimal.ZERO) > 0; + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + private void assertSameCurrency(Money other) { + if (!currency.equals(other.currency)) { + throw new IllegalArgumentException("Currency mismatch"); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Product.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Product.java new file mode 100644 index 0000000..752c902 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/Product.java @@ -0,0 +1,66 @@ +package pl.shop.catalog; + +import io.vavr.control.Either; + +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class Product { + + private final ProductId id; + private ProductName name; + private ProductStatus status; + + Product(ProductId id, ProductName name) { + this.id = id; + this.name = name; + this.status = ProductStatus.DRAFT; + } + + public static Product create(ProductName name) { + return new Product(ProductId.newOne(), name); + } + + public Either publish() { + if (status != ProductStatus.DRAFT) { + return left("Only draft products can be published"); + } + this.status = ProductStatus.PUBLISHED; + return right(new ProductPublished(id)); + } + + public Either archive() { + if (status == ProductStatus.ARCHIVED) { + return left("Product is already archived"); + } + this.status = ProductStatus.ARCHIVED; + return right(new ProductArchived(id)); + } + + public ProductId id() { + return id; + } + + public ProductName name() { + return name; + } + + public boolean isPublished() { + return status == ProductStatus.PUBLISHED; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Product product = (Product) o; + return Objects.equals(id, product.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductArchived.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductArchived.java new file mode 100644 index 0000000..1fe4184 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductArchived.java @@ -0,0 +1,29 @@ +package pl.shop.catalog; + +import java.util.Objects; + +public class ProductArchived { + + private final ProductId productId; + + ProductArchived(ProductId productId) { + this.productId = productId; + } + + public ProductId productId() { + return productId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProductArchived that = (ProductArchived) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductId.java new file mode 100644 index 0000000..a716bf5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog; + +import java.util.Objects; +import java.util.UUID; + +public final class ProductId { + + private final UUID value; + + public ProductId(UUID value) { + Objects.requireNonNull(value, "ProductId cannot be null"); + this.value = value; + } + + public static ProductId newOne() { + return new ProductId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProductId that = (ProductId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductName.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductName.java new file mode 100644 index 0000000..e805e6b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductName.java @@ -0,0 +1,35 @@ +package pl.shop.catalog; + +import java.util.Objects; + +public final class ProductName { + + private final String value; + + public ProductName(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Product name cannot be empty"); + } + if (value.length() > 200) { + throw new IllegalArgumentException("Product name cannot exceed 200 characters"); + } + this.value = value; + } + + public String value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProductName that = (ProductName) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductPublished.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductPublished.java new file mode 100644 index 0000000..057593c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductPublished.java @@ -0,0 +1,29 @@ +package pl.shop.catalog; + +import java.util.Objects; + +public class ProductPublished { + + private final ProductId productId; + + ProductPublished(ProductId productId) { + this.productId = productId; + } + + public ProductId productId() { + return productId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProductPublished that = (ProductPublished) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductRepository.java new file mode 100644 index 0000000..de2664f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductRepository.java @@ -0,0 +1,8 @@ +package pl.shop.catalog; + +import java.util.Optional; + +public interface ProductRepository { + void save(Product product); + Optional findById(ProductId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductStatus.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductStatus.java new file mode 100644 index 0000000..36c6dba --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/ProductStatus.java @@ -0,0 +1,5 @@ +package pl.shop.catalog; + +enum ProductStatus { + DRAFT, PUBLISHED, ARCHIVED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/ArchiveProductService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/ArchiveProductService.java new file mode 100644 index 0000000..a6e3725 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/ArchiveProductService.java @@ -0,0 +1,24 @@ +package pl.shop.catalog.publishing; + +import io.vavr.control.Either; +import pl.shop.catalog.Product; +import pl.shop.catalog.ProductArchived; +import pl.shop.catalog.ProductId; +import pl.shop.catalog.ProductRepository; + +import static io.vavr.control.Either.left; + +public class ArchiveProductService { + + private final ProductRepository productRepository; + + public ArchiveProductService(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public Either archive(ProductId productId) { + return productRepository.findById(productId) + .map(Product::archive) + .orElse(left("Product not found: " + productId)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/PublishProductService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/PublishProductService.java new file mode 100644 index 0000000..dd62324 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/catalog/publishing/PublishProductService.java @@ -0,0 +1,24 @@ +package pl.shop.catalog.publishing; + +import io.vavr.control.Either; +import pl.shop.catalog.Product; +import pl.shop.catalog.ProductId; +import pl.shop.catalog.ProductPublished; +import pl.shop.catalog.ProductRepository; + +import static io.vavr.control.Either.left; + +public class PublishProductService { + + private final ProductRepository productRepository; + + public PublishProductService(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public Either publish(ProductId productId) { + return productRepository.findById(productId) + .map(Product::publish) + .orElse(left("Product not found: " + productId)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryItem.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryItem.java new file mode 100644 index 0000000..495ead1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryItem.java @@ -0,0 +1,65 @@ +package pl.shop.inventory; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class InventoryItem { + + private final ProductId productId; + private StockLevel stockLevel; + + InventoryItem(ProductId productId, StockLevel stockLevel) { + this.productId = productId; + this.stockLevel = stockLevel; + } + + public static InventoryItem create(ProductId productId) { + return new InventoryItem(productId, StockLevel.zero()); + } + + public Either replenish(int quantity) { + if (quantity <= 0) { + return left("Replenish quantity must be positive"); + } + this.stockLevel = stockLevel.increase(quantity); + return right(new StockReplenished(productId, stockLevel)); + } + + public Either reserve(int quantity) { + if (!stockLevel.isAvailable()) { + return left("No stock available"); + } + try { + this.stockLevel = stockLevel.decrease(quantity); + return right(new StockReserved(productId, quantity)); + } catch (IllegalArgumentException e) { + return left("Insufficient stock"); + } + } + + public ProductId productId() { + return productId; + } + + public StockLevel stockLevel() { + return stockLevel; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InventoryItem that = (InventoryItem) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryRepository.java new file mode 100644 index 0000000..6500ddf --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/InventoryRepository.java @@ -0,0 +1,10 @@ +package pl.shop.inventory; + +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public interface InventoryRepository { + void save(InventoryItem item); + Optional findByProductId(ProductId productId); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/ReplenishStockService.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/ReplenishStockService.java new file mode 100644 index 0000000..bdd8a89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/ReplenishStockService.java @@ -0,0 +1,23 @@ +package pl.shop.inventory; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ReplenishStockService { + + private final InventoryRepository inventoryRepository; + + public ReplenishStockService(InventoryRepository inventoryRepository) { + this.inventoryRepository = inventoryRepository; + } + + public Either replenish(ProductId productId, int quantity) { + InventoryItem item = inventoryRepository.findByProductId(productId) + .orElse(InventoryItem.create(productId)); + Either result = item.replenish(quantity); + result.peek(event -> inventoryRepository.save(item)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockLevel.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockLevel.java new file mode 100644 index 0000000..6e851aa --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockLevel.java @@ -0,0 +1,55 @@ +package pl.shop.inventory; + +import java.util.Objects; + +public final class StockLevel { + + private final int quantity; + + public StockLevel(int quantity) { + if (quantity < 0) { + throw new IllegalArgumentException("Stock level cannot be negative"); + } + this.quantity = quantity; + } + + public static StockLevel of(int quantity) { + return new StockLevel(quantity); + } + + public static StockLevel zero() { + return new StockLevel(0); + } + + public StockLevel increase(int amount) { + return new StockLevel(quantity + amount); + } + + public StockLevel decrease(int amount) { + if (quantity - amount < 0) { + throw new IllegalArgumentException("Cannot decrease below zero"); + } + return new StockLevel(quantity - amount); + } + + public boolean isAvailable() { + return quantity > 0; + } + + public int quantity() { + return quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockLevel that = (StockLevel) o; + return quantity == that.quantity; + } + + @Override + public int hashCode() { + return Objects.hash(quantity); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReplenished.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReplenished.java new file mode 100644 index 0000000..945463b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReplenished.java @@ -0,0 +1,37 @@ +package pl.shop.inventory; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class StockReplenished { + + private final ProductId productId; + private final StockLevel newLevel; + + StockReplenished(ProductId productId, StockLevel newLevel) { + this.productId = productId; + this.newLevel = newLevel; + } + + public ProductId productId() { + return productId; + } + + public StockLevel newLevel() { + return newLevel; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockReplenished that = (StockReplenished) o; + return Objects.equals(productId, that.productId) && Objects.equals(newLevel, that.newLevel); + } + + @Override + public int hashCode() { + return Objects.hash(productId, newLevel); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReserved.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReserved.java new file mode 100644 index 0000000..750abc3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-plain-java/src/main/java/pl/shop/inventory/StockReserved.java @@ -0,0 +1,37 @@ +package pl.shop.inventory; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class StockReserved { + + private final ProductId productId; + private final int quantity; + + StockReserved(ProductId productId, int quantity) { + this.productId = productId; + this.quantity = quantity; + } + + public ProductId productId() { + return productId; + } + + public int quantity() { + return quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockReserved that = (StockReserved) o; + return quantity == that.quantity && Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId, quantity); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/build.gradle.kts b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/build.gradle.kts new file mode 100644 index 0000000..eae7393 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + java +} + +group = "dev.app" +version = "1.0-SNAPSHOT" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +repositories { + mavenCentral() +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Account.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Account.java new file mode 100644 index 0000000..7d79237 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Account.java @@ -0,0 +1,60 @@ +package dev.app.banking.accounts; + +import java.util.ArrayList; +import java.util.List; + +public final class Account { + + sealed interface Event permits Deposited, Withdrawn, AccountOpened {} + record AccountOpened(AccountId accountId, Amount initialBalance) implements Event {} + record Deposited(AccountId accountId, Amount amount) implements Event {} + record Withdrawn(AccountId accountId, Amount amount) implements Event {} + + private final AccountId id; + private Amount balance; + private final List pendingEvents = new ArrayList<>(); + + private Account(AccountId id, Amount balance) { + this.id = id; + this.balance = balance; + } + + public static Account open(Amount initialDeposit) { + if (initialDeposit.isNegative()) { + throw new IllegalArgumentException("Initial deposit cannot be negative"); + } + var id = AccountId.generate(); + var account = new Account(id, initialDeposit); + account.pendingEvents.add(new AccountOpened(id, initialDeposit)); + return account; + } + + public void deposit(Amount amount) { + if (amount.isNegative()) { + throw new IllegalArgumentException("Deposit amount must be positive"); + } + this.balance = balance.add(amount); + pendingEvents.add(new Deposited(id, amount)); + } + + public void withdraw(Amount amount) { + if (amount.isNegative()) { + throw new IllegalArgumentException("Withdrawal amount must be positive"); + } + var newBalance = balance.subtract(amount); + if (newBalance.isNegative()) { + throw new IllegalStateException("Insufficient funds"); + } + this.balance = newBalance; + pendingEvents.add(new Withdrawn(id, amount)); + } + + public AccountId id() { return id; } + public Amount balance() { return balance; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountId.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountId.java new file mode 100644 index 0000000..cb78acc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountId.java @@ -0,0 +1,14 @@ +package dev.app.banking.accounts; + +import java.util.UUID; + +public record AccountId(UUID value) { + + public AccountId { + if (value == null) throw new IllegalArgumentException("AccountId cannot be null"); + } + + public static AccountId generate() { + return new AccountId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountRepository.java new file mode 100644 index 0000000..bb65f16 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/AccountRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.accounts; + +import java.util.Optional; + +public interface AccountRepository { + void save(Account account); + Optional findById(AccountId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Amount.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Amount.java new file mode 100644 index 0000000..5a31f8b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/Amount.java @@ -0,0 +1,30 @@ +package dev.app.banking.accounts; + +import java.math.BigDecimal; + +public record Amount(BigDecimal value) { + + public Amount { + if (value == null) throw new IllegalArgumentException("Amount cannot be null"); + } + + public static Amount of(BigDecimal value) { + return new Amount(value); + } + + public static Amount zero() { + return new Amount(BigDecimal.ZERO); + } + + public Amount add(Amount other) { + return new Amount(value.add(other.value)); + } + + public Amount subtract(Amount other) { + return new Amount(value.subtract(other.value)); + } + + public boolean isNegative() { + return value.compareTo(BigDecimal.ZERO) < 0; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositCommand.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositCommand.java new file mode 100644 index 0000000..bdc5ac8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositCommand.java @@ -0,0 +1,9 @@ +package dev.app.banking.accounts; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record DepositCommand(UUID accountId, BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositHandler.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositHandler.java new file mode 100644 index 0000000..4c7c3dc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/DepositHandler.java @@ -0,0 +1,23 @@ +package dev.app.banking.accounts; + +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class DepositHandler implements CommandHandler { + + private final AccountRepository accountRepository; + + public DepositHandler(AccountRepository accountRepository) { + this.accountRepository = accountRepository; + } + + @Override + public Void handle(DepositCommand command) { + var accountId = new AccountId(command.accountId()); + var account = accountRepository.findById(accountId) + .orElseThrow(() -> new DomainException("Account not found: " + accountId)); + account.deposit(Amount.of(command.amount())); + accountRepository.save(account); + return null; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountCommand.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountCommand.java new file mode 100644 index 0000000..f2fb377 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountCommand.java @@ -0,0 +1,8 @@ +package dev.app.banking.accounts; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; + +public record OpenAccountCommand(BigDecimal initialDeposit) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountHandler.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountHandler.java new file mode 100644 index 0000000..005d141 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/accounts/OpenAccountHandler.java @@ -0,0 +1,19 @@ +package dev.app.banking.accounts; + +import dev.app.banking.shared.CommandHandler; + +public final class OpenAccountHandler implements CommandHandler { + + private final AccountRepository accountRepository; + + public OpenAccountHandler(AccountRepository accountRepository) { + this.accountRepository = accountRepository; + } + + @Override + public AccountId handle(OpenAccountCommand command) { + var account = Account.open(Amount.of(command.initialDeposit())); + accountRepository.save(account); + return account.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/DailyLimit.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/DailyLimit.java new file mode 100644 index 0000000..f6a08e2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/DailyLimit.java @@ -0,0 +1,18 @@ +package dev.app.banking.limits; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +public record DailyLimit(AccountId accountId, Amount maxAmount) { + + public DailyLimit { + if (maxAmount.isNegative()) { + throw new IllegalArgumentException("Daily limit cannot be negative"); + } + } + + public boolean allows(Amount transferAmount) { + return !transferAmount.value().subtract(maxAmount.value()).stripTrailingZeros() + .equals(transferAmount.value().stripTrailingZeros()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/LimitRepository.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/LimitRepository.java new file mode 100644 index 0000000..2a67dbc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/limits/LimitRepository.java @@ -0,0 +1,10 @@ +package dev.app.banking.limits; + +import dev.app.banking.accounts.AccountId; + +import java.util.Optional; + +public interface LimitRepository { + Optional findByAccountId(AccountId accountId); + void save(DailyLimit limit); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/Command.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/Command.java new file mode 100644 index 0000000..3b17b74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/Command.java @@ -0,0 +1,4 @@ +package dev.app.banking.shared; + +public interface Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/CommandHandler.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/CommandHandler.java new file mode 100644 index 0000000..cb80f39 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/CommandHandler.java @@ -0,0 +1,5 @@ +package dev.app.banking.shared; + +public interface CommandHandler, R> { + R handle(C command); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/DomainException.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/DomainException.java new file mode 100644 index 0000000..3136973 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-records-modern/src/main/java/dev/app/banking/shared/DomainException.java @@ -0,0 +1,8 @@ +package dev.app.banking.shared; + +public class DomainException extends RuntimeException { + + public DomainException(String message) { + super(message); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/pom.xml new file mode 100644 index 0000000..cd9b485 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + io.proj + warehouse + 1.0-SNAPSHOT + + 21 + 21 + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryEvents.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryEvents.java new file mode 100644 index 0000000..715ddd5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryEvents.java @@ -0,0 +1,12 @@ +package io.proj.warehouse.inventory; + +import java.time.Instant; + +public sealed interface InventoryEvents { + + record StockReceived(Sku sku, Quantity quantity, Instant occurredAt) implements InventoryEvents {} + + record StockAdjusted(Sku sku, Quantity oldQuantity, Quantity newQuantity, String reason, Instant occurredAt) implements InventoryEvents {} + + record StockDepleted(Sku sku, Instant occurredAt) implements InventoryEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryItem.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryItem.java new file mode 100644 index 0000000..d58e67a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/InventoryItem.java @@ -0,0 +1,63 @@ +package io.proj.warehouse.inventory; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class InventoryItem { + + private final Sku sku; + private Quantity onHand; + private final List pendingEvents = new ArrayList<>(); + + private InventoryItem(Sku sku, Quantity onHand) { + this.sku = sku; + this.onHand = onHand; + } + + public static InventoryItem register(Sku sku) { + return new InventoryItem(sku, Quantity.zero()); + } + + public void receive(Quantity quantity) { + this.onHand = onHand.add(quantity); + pendingEvents.add(new InventoryEvents.StockReceived(sku, quantity, Instant.now())); + } + + public void adjust(Quantity newQuantity, String reason) { + var old = this.onHand; + this.onHand = newQuantity; + pendingEvents.add(new InventoryEvents.StockAdjusted(sku, old, newQuantity, reason, Instant.now())); + if (newQuantity.isZero()) { + pendingEvents.add(new InventoryEvents.StockDepleted(sku, Instant.now())); + } + } + + public Sku sku() { return sku; } + public Quantity onHand() { return onHand; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(InventoryItem item); + InventoryItem findBySku(Sku sku); + } + + public static class Factory { + private final Repository repository; + + public Factory(Repository repository) { + this.repository = repository; + } + + public InventoryItem registerNew(Sku sku) { + var item = InventoryItem.register(sku); + repository.save(item); + return item; + } + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Quantity.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Quantity.java new file mode 100644 index 0000000..495e19d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Quantity.java @@ -0,0 +1,11 @@ +package io.proj.warehouse.inventory; + +public record Quantity(int value) { + public Quantity { + if (value < 0) throw new IllegalArgumentException("Quantity cannot be negative"); + } + public static Quantity zero() { return new Quantity(0); } + public Quantity add(Quantity other) { return new Quantity(value + other.value); } + public Quantity subtract(Quantity other) { return new Quantity(value - other.value); } + public boolean isZero() { return value == 0; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/ReceiveStockHandler.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/ReceiveStockHandler.java new file mode 100644 index 0000000..adbead4 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/ReceiveStockHandler.java @@ -0,0 +1,16 @@ +package io.proj.warehouse.inventory; + +public final class ReceiveStockHandler { + + private final InventoryItem.Repository repository; + + public ReceiveStockHandler(InventoryItem.Repository repository) { + this.repository = repository; + } + + public void handle(Sku sku, Quantity quantity) { + var item = repository.findBySku(sku); + item.receive(quantity); + repository.save(item); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Sku.java b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Sku.java new file mode 100644 index 0000000..3fc72f3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/evals/fixtures/style-unusual-conventions/src/main/java/io/proj/warehouse/inventory/Sku.java @@ -0,0 +1,10 @@ +package io.proj.warehouse.inventory; + +import java.util.Objects; + +public record Sku(String value) { + public Sku { + Objects.requireNonNull(value); + if (value.isBlank()) throw new IllegalArgumentException("SKU cannot be blank"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/grade_eval.py b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/grade_eval.py new file mode 100644 index 0000000..fffe9f2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/grade_eval.py @@ -0,0 +1,798 @@ +# /// script +# dependencies = [] +# /// + +"""Programmatic grader for implement-design-doc-java evals. + +Checks structural assertions by scanning generated Java files. +""" + +import json +import os +import re +import sys +from pathlib import Path + + +def find_java_files(directory: str) -> list[Path]: + return sorted(Path(directory).rglob("*.java")) + + +def read_all_java(directory: str) -> dict[str, str]: + result = {} + for f in find_java_files(directory): + result[f.name] = f.read_text() + return result + + +def file_contains(files: dict[str, str], pattern: str) -> bool: + for content in files.values(): + if re.search(pattern, content): + return True + return False + + +def any_file_named(files: dict[str, str], name: str) -> bool: + return name in files + + +def file_content(files: dict[str, str], name: str) -> str: + return files.get(name, "") + + +# --- Eval 1: Shipping Lombok Spring --- + +def grade_shipping(files: dict[str, str]) -> list[dict]: + results = [] + + # 1. correct_package_placement + has_domain_pkg = file_contains(files, r"package\s+com\.example\.orders\.shipping\.domain") + has_app_pkg = file_contains(files, r"package\s+com\.example\.orders\.shipping\.application") + results.append({ + "text": "correct_package_placement", + "passed": has_domain_pkg and has_app_pkg, + "evidence": f"domain pkg: {has_domain_pkg}, application pkg: {has_app_pkg}" + }) + + # 2. facade_pattern_followed + facade_files = [n for n in files if "Facade" in n and "Shipping" in n] + has_facade = len(facade_files) > 0 + facade_public = False + if has_facade: + fc = file_content(files, facade_files[0]) + facade_public = "public class" in fc or "public final class" in fc + results.append({ + "text": "facade_pattern_followed", + "passed": has_facade and facade_public, + "evidence": f"facade files: {facade_files}, public: {facade_public}" + }) + + # 3. spring_configuration_present + config_files = [n for n in files if "Configuration" in n and "Shipping" in n] + has_config = len(config_files) > 0 + has_bean = False + if has_config: + cc = file_content(files, config_files[0]) + has_bean = "@Bean" in cc and "@Configuration" in cc + results.append({ + "text": "spring_configuration_present", + "passed": has_config and has_bean, + "evidence": f"config files: {config_files}, @Bean+@Configuration: {has_bean}" + }) + + # 4. lombok_value_for_vos + vo_names = ["ShipmentId.java", "ShippingAddress.java", "TrackingNumber.java"] + vos_with_value = 0 + for vo in vo_names: + c = file_content(files, vo) + if "@Value" in c: + vos_with_value += 1 + results.append({ + "text": "lombok_value_for_vos", + "passed": vos_with_value >= 2, + "evidence": f"{vos_with_value}/{len(vo_names)} VOs use @Value" + }) + + # 5. domain_event_interface_reused + reuses_event = file_contains(files, r"import\s+com\.example\.orders\.domain\.DomainEvent") + creates_new = any_file_named(files, "DomainEvent.java") + results.append({ + "text": "domain_event_interface_reused", + "passed": reuses_event and not creates_new, + "evidence": f"imports existing DomainEvent: {reuses_event}, creates new: {creates_new}" + }) + + # 6. pending_events_pattern + shipment = file_content(files, "Shipment.java") + has_pending = "pendingEvents" in shipment or "pending_events" in shipment + has_flush = "flushEvents" in shipment or "flush" in shipment.lower() + results.append({ + "text": "pending_events_pattern", + "passed": has_pending and has_flush, + "evidence": f"pendingEvents: {has_pending}, flushEvents: {has_flush}" + }) + + # 7. all_building_blocks_present + expected = ["Shipment", "ShipmentId", "ShippingAddress", "TrackingNumber", + "ShipmentCreated", "ShipmentDispatched", "ShipmentRepository", "ShipmentStatus"] + found = [e for e in expected if any(e in n for n in files)] + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) == len(expected), + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. business_rules_enforced + shipment = file_content(files, "Shipment.java") + # Check tracking number guard in dispatch(): look for null check or any conditional on trackingNumber + has_tracking_check = ("trackingNumber" in shipment and + ("null" in shipment or "== null" in shipment or "!= null" in shipment + or "hasTracking" in shipment or "assigned" in shipment.lower() + or "tracking number" in shipment.lower())) + # Check status guard: delivered/dispatched status checks + has_status_check = ("DELIVERED" in shipment or "Delivered" in shipment) and ("throw" in shipment or "Exception" in shipment) + results.append({ + "text": "business_rules_enforced", + "passed": has_tracking_check and has_status_check, + "evidence": f"tracking check: {has_tracking_check}, status check: {has_status_check}" + }) + + # 9. orderid_reused_not_recreated + imports_orderid = file_contains(files, r"import\s+com\.example\.orders\.domain\.OrderId") + creates_orderid = any_file_named(files, "OrderId.java") + results.append({ + "text": "orderid_reused_not_recreated", + "passed": imports_orderid and not creates_orderid, + "evidence": f"imports OrderId: {imports_orderid}, creates new: {creates_orderid}" + }) + + return results + + +# --- Eval 2: Pricing Plain Java --- + +def grade_pricing(files: dict[str, str]) -> list[dict]: + results = [] + + # 1. correct_package_placement + has_pricing_pkg = file_contains(files, r"package\s+pl\.shop\.catalog\.pricing") + results.append({ + "text": "correct_package_placement", + "passed": has_pricing_pkg, + "evidence": f"pricing package: {has_pricing_pkg}" + }) + + # 2. service_per_use_case_pattern + service_files = [n for n in files if "Service" in n] + has_multiple_services = len(service_files) >= 2 + results.append({ + "text": "service_per_use_case_pattern", + "passed": has_multiple_services, + "evidence": f"service files: {service_files}" + }) + + # 3. vavr_either_returns + has_either = file_contains(files, r"Either") or file_contains(files, r"Either") + has_vavr_import = file_contains(files, r"import\s+io\.vavr") + results.append({ + "text": "vavr_either_returns", + "passed": has_either and has_vavr_import, + "evidence": f"Either returns: {has_either}, Vavr import: {has_vavr_import}" + }) + + # 4. no_lombok_used + has_lombok = file_contains(files, r"import\s+lombok") + results.append({ + "text": "no_lombok_used", + "passed": not has_lombok, + "evidence": f"lombok imports found: {has_lombok}" + }) + + # 5. money_reused_not_recreated + imports_money = file_contains(files, r"import\s+pl\.shop\.catalog\.Money") + creates_money = any_file_named(files, "Money.java") + results.append({ + "text": "money_reused_not_recreated", + "passed": imports_money and not creates_money, + "evidence": f"imports Money: {imports_money}, creates new: {creates_money}" + }) + + # 6. productid_reused_not_recreated + imports_pid = file_contains(files, r"import\s+pl\.shop\.catalog\.ProductId") + creates_pid = any_file_named(files, "ProductId.java") + results.append({ + "text": "productid_reused_not_recreated", + "passed": imports_pid and not creates_pid, + "evidence": f"imports ProductId: {imports_pid}, creates new: {creates_pid}" + }) + + # 7. all_building_blocks_present + expected = ["PriceList", "PriceListId", "PriceEntry", "Discount", + "PricingService", "DiscountApplied", "PriceListActivated"] + # Money already exists, so 7 new classes expected + found = [e for e in expected if any(e in n for n in files)] + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) >= 6, + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. discount_percentage_validation + discount = file_content(files, "Discount.java") + has_validation = ("100" in discount or "percent" in discount.lower()) and ("throw" in discount or "left" in discount) + results.append({ + "text": "discount_percentage_validation", + "passed": has_validation, + "evidence": f"validation in Discount: {has_validation}" + }) + + # 9. package_private_internals + price_entry = file_content(files, "PriceEntry.java") + is_package_private = price_entry and "public class PriceEntry" not in price_entry + results.append({ + "text": "package_private_internals", + "passed": is_package_private, + "evidence": f"PriceEntry package-private: {is_package_private}" + }) + + return results + + +# --- Eval 3: Transfers Records Modern --- + +def grade_transfers(files: dict[str, str]) -> list[dict]: + results = [] + + # 1. correct_package_placement + has_transfers_pkg = file_contains(files, r"package\s+dev\.app\.banking\.transfers") + results.append({ + "text": "correct_package_placement", + "passed": has_transfers_pkg, + "evidence": f"transfers package: {has_transfers_pkg}" + }) + + # 2. command_handler_pattern + cmd_files = [n for n in files if "Command" in n and "Transfer" in n] + handler_files = [n for n in files if "Handler" in n and "Transfer" in n] + has_implements_command = file_contains(files, r"implements\s+Command<") + has_implements_handler = file_contains(files, r"implements\s+CommandHandler<") + results.append({ + "text": "command_handler_pattern", + "passed": len(cmd_files) >= 1 and len(handler_files) >= 1 and has_implements_command and has_implements_handler, + "evidence": f"commands: {cmd_files}, handlers: {handler_files}, implements: cmd={has_implements_command}, handler={has_implements_handler}" + }) + + # 3. records_for_value_objects + transfer_id = file_content(files, "TransferId.java") + is_record = "record TransferId" in transfer_id + results.append({ + "text": "records_for_value_objects", + "passed": is_record, + "evidence": f"TransferId is record: {is_record}" + }) + + # 4. sealed_event_interface + transfer = file_content(files, "Transfer.java") + has_sealed = "sealed" in transfer and "Event" in transfer and "permits" in transfer + results.append({ + "text": "sealed_event_interface", + "passed": has_sealed, + "evidence": f"sealed Event interface: {has_sealed}" + }) + + # 5. accountid_reused_not_recreated + imports_aid = file_contains(files, r"import\s+dev\.app\.banking\.accounts\.AccountId") + creates_aid = any_file_named(files, "AccountId.java") + results.append({ + "text": "accountid_reused_not_recreated", + "passed": imports_aid and not creates_aid, + "evidence": f"imports AccountId: {imports_aid}, creates new: {creates_aid}" + }) + + # 6. amount_reused_not_recreated + imports_amount = file_contains(files, r"import\s+dev\.app\.banking\.accounts\.Amount") + creates_amount = any_file_named(files, "Amount.java") + results.append({ + "text": "amount_reused_not_recreated", + "passed": imports_amount and not creates_amount, + "evidence": f"imports Amount: {imports_amount}, creates new: {creates_amount}" + }) + + # 7. all_building_blocks_present + expected = ["Transfer", "TransferId", "TransferStatus", + "TransferInitiated", "TransferCompleted", "TransferRepository"] + # AccountId and Amount already exist + # Check both filenames AND class/record definitions inside files (for inner records) + all_content = " ".join(files.values()) + found = [] + for e in expected: + if any(e in n for n in files): + found.append(e) + elif re.search(rf"\b(class|record|interface|enum)\s+{e}\b", all_content): + found.append(e + " (inner)") + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) >= 5, + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. same_account_validation + transfer = file_content(files, "Transfer.java") + all_content = " ".join(files.values()) + has_same_check = ("source" in all_content.lower() and "destination" in all_content.lower() + and ("equals" in all_content or "==" in all_content or "same" in all_content.lower())) + results.append({ + "text": "same_account_validation", + "passed": has_same_check, + "evidence": f"same-account validation: {has_same_check}" + }) + + # 9. modern_java_features + has_var = file_contains(files, r"\bvar\s+\w+") + has_record = file_contains(files, r"\brecord\s+\w+") + results.append({ + "text": "modern_java_features", + "passed": has_var and has_record, + "evidence": f"var: {has_var}, record: {has_record}" + }) + + return results + + +# --- Eval 4: Payroll Mixed Patterns --- + +def grade_payroll(files: dict[str, str]) -> list[dict]: + results = [] + all_content = " ".join(files.values()) + + # 1. correct_package_placement + has_payroll_pkg = file_contains(files, r"package\s+com\.acme\.hr\.payroll") + results.append({ + "text": "correct_package_placement", + "passed": has_payroll_pkg, + "evidence": f"payroll package: {has_payroll_pkg}" + }) + + # 2. uses_rich_model_not_anemic — KEY DISCRIMINATOR + # Should follow leave module style (Either returns, immutable VOs, private constructors) + # NOT employees style (getters/setters, exceptions, mutable) + payroll_run = file_content(files, "PayrollRun.java") + has_either = file_contains(files, r"Either") or file_contains(files, r"Either") + has_vavr = file_contains(files, r"import\s+io\.vavr") + has_no_setters = "void set" not in payroll_run + results.append({ + "text": "uses_rich_model_not_anemic", + "passed": has_either and has_vavr and has_no_setters, + "evidence": f"Either: {has_either}, Vavr import: {has_vavr}, no setters: {has_no_setters}" + }) + + # 3. immutable_value_objects — KEY DISCRIMINATOR + # Salary should be final class with private constructor (leave style) not mutable bean (employees style) + salary = file_content(files, "Salary.java") + is_final_or_record = "final class Salary" in salary or "record Salary" in salary + has_no_setter = "void set" not in salary + results.append({ + "text": "immutable_value_objects", + "passed": is_final_or_record and has_no_setter, + "evidence": f"final/record: {is_final_or_record}, no setters: {has_no_setter}" + }) + + # 4. service_per_use_case — KEY DISCRIMINATOR + # Should follow leave style (SubmitLeaveService, ApproveLeaveService) not EmployeeService (god service) + service_files = [n for n in files if "Service" in n] + has_specific_services = len(service_files) >= 1 + # Should NOT have a god service like "PayrollService" doing everything + has_god_service = any("PayrollService.java" == n for n in files) + results.append({ + "text": "service_per_use_case", + "passed": has_specific_services and not has_god_service, + "evidence": f"services: {service_files}, god service: {has_god_service}" + }) + + # 5. domain_application_layer_split + has_domain = file_contains(files, r"package\s+com\.acme\.hr\.payroll\.domain") + has_app = file_contains(files, r"package\s+com\.acme\.hr\.payroll\.application") + results.append({ + "text": "domain_application_layer_split", + "passed": has_domain and has_app, + "evidence": f"domain pkg: {has_domain}, application pkg: {has_app}" + }) + + # 6. employeeid_reused + imports_eid = file_contains(files, r"import\s+com\.acme\.hr\.employees\.EmployeeId") + creates_eid = any_file_named(files, "EmployeeId.java") + results.append({ + "text": "employeeid_reused", + "passed": imports_eid and not creates_eid, + "evidence": f"imports EmployeeId: {imports_eid}, creates new: {creates_eid}" + }) + + # 7. all_building_blocks_present + expected = ["PayrollRun", "PayrollRunId", "PayrollStatus", "Salary", + "Payslip", "PayrollExecuted", "PayrollRunRepository"] + found = [e for e in expected if any(e in n for n in files) or + re.search(rf"\b(class|record|interface|enum)\s+{e}\b", all_content)] + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) >= 6, + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. payslip_package_private — entity within aggregate + payslip = file_content(files, "Payslip.java") + is_pkg_private = payslip and "public class Payslip" not in payslip + results.append({ + "text": "payslip_package_private", + "passed": is_pkg_private, + "evidence": f"Payslip package-private: {is_pkg_private}" + }) + + # 9. business_rules_enforced + payroll_run = file_content(files, "PayrollRun.java") + has_empty_check = "empty" in payroll_run.lower() or "payslips" in payroll_run.lower() + has_executed_guard = "EXECUTED" in payroll_run or "Executed" in payroll_run + results.append({ + "text": "business_rules_enforced", + "passed": has_empty_check and has_executed_guard, + "evidence": f"empty check: {has_empty_check}, executed guard: {has_executed_guard}" + }) + + return results + + +# --- Eval 5: Order Extended (Delta) --- + +def grade_order_delta(files: dict[str, str]) -> list[dict]: + results = [] + all_content = " ".join(files.values()) + + # 1. order_modified_not_recreated — KEY DISCRIMINATOR + # Order.java should be in outputs but should EXTEND existing, not start from scratch + order = file_content(files, "Order.java") + has_order = bool(order) + # Must have BOTH old methods (addLine, place, total) AND new ones (cancel, applyDiscount) + has_old_methods = "addLine" in order and "place" in order and "total()" in order + has_new_methods = "cancel" in order and "applyDiscount" in order + results.append({ + "text": "order_modified_not_recreated", + "passed": has_order and has_old_methods and has_new_methods, + "evidence": f"has Order: {has_order}, old methods: {has_old_methods}, new methods: {has_new_methods}" + }) + + # 2. status_enum_extended — KEY DISCRIMINATOR + # Status enum should have CANCELLED added to existing DRAFT, PLACED + has_cancelled = "CANCELLED" in order or "Cancelled" in order + has_draft = "DRAFT" in order or "Draft" in order + has_placed = "PLACED" in order or "Placed" in order + results.append({ + "text": "status_enum_extended", + "passed": has_cancelled and has_draft and has_placed, + "evidence": f"CANCELLED: {has_cancelled}, DRAFT: {has_draft}, PLACED: {has_placed}" + }) + + # 3. new_fields_added + has_shipping = "shippingAddress" in order or "shipping_address" in order.lower() + has_discount = "discountCode" in order or "discount" in order.lower() + results.append({ + "text": "new_fields_added", + "passed": has_shipping and has_discount, + "evidence": f"shippingAddress: {has_shipping}, discountCode: {has_discount}" + }) + + # 4. new_vo_classes_created + has_discount_code = any_file_named(files, "DiscountCode.java") + has_shipping_addr = any_file_named(files, "ShippingAddress.java") + results.append({ + "text": "new_vo_classes_created", + "passed": has_discount_code and has_shipping_addr, + "evidence": f"DiscountCode: {has_discount_code}, ShippingAddress: {has_shipping_addr}" + }) + + # 5. discount_validation + discount_code = file_content(files, "DiscountCode.java") + has_pct_validation = "100" in discount_code and ("throw" in discount_code or "left" in discount_code) + results.append({ + "text": "discount_validation", + "passed": has_pct_validation, + "evidence": f"percentage validation: {has_pct_validation}" + }) + + # 6. shipping_address_validation + shipping = file_content(files, "ShippingAddress.java") + has_addr_validation = shipping and ("street" in shipping and ("blank" in shipping.lower() or "null" in shipping or "empty" in shipping.lower())) + results.append({ + "text": "shipping_address_validation", + "passed": bool(has_addr_validation), + "evidence": f"address validation: {has_addr_validation}" + }) + + # 7. cancel_rule_enforced — only placed orders + has_cancel_guard = "cancel" in order and ("PLACED" in order or "Placed" in order or "isPlaced" in order) + results.append({ + "text": "cancel_rule_enforced", + "passed": has_cancel_guard, + "evidence": f"cancel only-placed guard: {has_cancel_guard}" + }) + + # 8. order_cancelled_event + has_event = any_file_named(files, "OrderCancelled.java") or "OrderCancelled" in all_content + results.append({ + "text": "order_cancelled_event", + "passed": has_event, + "evidence": f"OrderCancelled event: {has_event}" + }) + + # 9. cancel_order_service + has_cancel_service = any_file_named(files, "CancelOrderService.java") + results.append({ + "text": "cancel_order_service", + "passed": has_cancel_service, + "evidence": f"CancelOrderService: {has_cancel_service}" + }) + + # 10. existing_types_not_duplicated — KEY DISCRIMINATOR + # Should NOT create OrderId, CustomerId, Money in a DIFFERENT package + # Copying them to outputs in the SAME package (org.store.orders.domain) is OK + original_pkg = "org.store.orders.domain" + duplicated = [] + for name in ["OrderId.java", "CustomerId.java", "Money.java"]: + content = file_content(files, name) + if content: + # Check if it's in the original package (OK) or a different one (BAD) + if original_pkg not in content: + duplicated.append(name) + results.append({ + "text": "existing_types_not_duplicated", + "passed": len(duplicated) == 0, + "evidence": f"duplicated in wrong package: {duplicated}" if duplicated else "all existing types in correct package or not emitted" + }) + + # 11. records_style_preserved — new VOs should use records like existing project + discount_code = file_content(files, "DiscountCode.java") + shipping = file_content(files, "ShippingAddress.java") + dc_is_record = "record DiscountCode" in discount_code + sa_is_record = "record ShippingAddress" in shipping + results.append({ + "text": "records_style_preserved", + "passed": dc_is_record and sa_is_record, + "evidence": f"DiscountCode record: {dc_is_record}, ShippingAddress record: {sa_is_record}" + }) + + return results + + +def grade_run(eval_name: str, output_dir: str) -> list[dict]: + files = read_all_java(output_dir) + if not files: + return [{"text": f"no_files_found", "passed": False, "evidence": f"No .java files in {output_dir}"}] + + if "shipping" in eval_name: + return grade_shipping(files) + elif "pricing" in eval_name: + return grade_pricing(files) + elif "transfers" in eval_name: + return grade_transfers(files) + elif "payroll" in eval_name: + return grade_payroll(files) + elif "order-delta" in eval_name or "order_delta" in eval_name: + return grade_order_delta(files) + elif "receiving" in eval_name: + return grade_receiving(files) + elif "returns" in eval_name: + return grade_returns(files) + else: + return [{"text": "unknown_eval", "passed": False, "evidence": f"Unknown eval: {eval_name}"}] + + +# --- Eval 6: Receiving (Unusual Conventions) --- + +def grade_receiving(files: dict[str, str]) -> list[dict]: + results = [] + all_content = " ".join(files.values()) + + # 1. correct_package + has_receiving_pkg = file_contains(files, r"package\s+io\.proj\.warehouse\.receiving") + results.append({ + "text": "correct_package_placement", + "passed": has_receiving_pkg, + "evidence": f"receiving package: {has_receiving_pkg}" + }) + + # 2. sealed_events_in_separate_file — KEY DISCRIMINATOR + # Project uses InventoryEvents.java as a separate sealed interface file + # New module should have ReceivingEvents.java (or similar), NOT inner records + event_files = [n for n in files if "Event" in n and "Receiving" in n] + has_sealed_events_file = len(event_files) > 0 + has_sealed_keyword = False + for ef in event_files: + if "sealed" in file_content(files, ef): + has_sealed_keyword = True + results.append({ + "text": "sealed_events_in_separate_file", + "passed": has_sealed_events_file and has_sealed_keyword, + "evidence": f"event files: {event_files}, sealed: {has_sealed_keyword}" + }) + + # 3. repository_as_nested_interface — KEY DISCRIMINATOR + # Project uses InventoryItem.Repository as nested interface + receiving_note = file_content(files, "ReceivingNote.java") + has_nested_repo = "interface Repository" in receiving_note + results.append({ + "text": "repository_as_nested_interface", + "passed": has_nested_repo, + "evidence": f"nested Repository interface in ReceivingNote: {has_nested_repo}" + }) + + # 4. sku_reused + imports_sku = file_contains(files, r"import\s+io\.proj\.warehouse\.inventory\.Sku") + creates_sku = any_file_named(files, "Sku.java") + results.append({ + "text": "sku_reused", + "passed": imports_sku and not creates_sku, + "evidence": f"imports Sku: {imports_sku}, creates new: {creates_sku}" + }) + + # 6. quantity_reused + imports_qty = file_contains(files, r"import\s+io\.proj\.warehouse\.inventory\.Quantity") + creates_qty = any_file_named(files, "Quantity.java") + results.append({ + "text": "quantity_reused", + "passed": imports_qty and not creates_qty, + "evidence": f"imports Quantity: {imports_qty}, creates new: {creates_qty}" + }) + + # 7. all_building_blocks + expected = ["ReceivingNote", "ReceivingNoteId", "ReceivedLine", "ReceivingStatus", + "ReceivingFinalized", "FinalizeReceivingHandler"] + found = [e for e in expected if any(e in n for n in files) or + re.search(rf"\b(class|record|interface|enum)\s+{e}\b", all_content)] + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) >= 5, + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. received_line_package_private + received_line = file_content(files, "ReceivedLine.java") + is_pkg_private = received_line and "public class ReceivedLine" not in received_line and "public record ReceivedLine" not in received_line + results.append({ + "text": "received_line_package_private", + "passed": bool(is_pkg_private), + "evidence": f"ReceivedLine package-private: {is_pkg_private}" + }) + + # 9. business_rules_enforced + rn = file_content(files, "ReceivingNote.java") + has_empty_check = "empty" in rn.lower() or "lines" in rn.lower() + has_finalized_guard = "FINALIZED" in rn or "Finalized" in rn + results.append({ + "text": "business_rules_enforced", + "passed": has_empty_check and has_finalized_guard, + "evidence": f"empty check: {has_empty_check}, finalized guard: {has_finalized_guard}" + }) + + return results + + +# --- Eval 7: Returns (Cross-Module) --- + +def grade_returns(files: dict[str, str]) -> list[dict]: + results = [] + all_content = " ".join(files.values()) + + # 1. return_request_in_sales — KEY DISCRIMINATOR + has_sales_pkg = file_contains(files, r"package\s+net\.ecom\.sales\.returns") + results.append({ + "text": "return_request_in_sales_package", + "passed": has_sales_pkg, + "evidence": f"sales.returns package: {has_sales_pkg}" + }) + + # 2. return_receipt_in_fulfillment — KEY DISCRIMINATOR + has_fulfillment_pkg = file_contains(files, r"package\s+net\.ecom\.fulfillment") + results.append({ + "text": "return_receipt_in_fulfillment_package", + "passed": has_fulfillment_pkg, + "evidence": f"fulfillment package: {has_fulfillment_pkg}" + }) + + # 3. sealed_events_per_aggregate — match project pattern + # Each aggregate should have its own sealed Event interface + has_return_request_events = "sealed" in all_content and "ReturnRequested" in all_content + has_return_receipt_events = "sealed" in all_content and "ReturnReceived" in all_content + results.append({ + "text": "sealed_events_pattern", + "passed": has_return_request_events and has_return_receipt_events, + "evidence": f"ReturnRequest events: {has_return_request_events}, ReturnReceipt events: {has_return_receipt_events}" + }) + + # 4. repository_as_nested_interface — match project pattern + return_request = file_content(files, "ReturnRequest.java") + return_receipt = file_content(files, "ReturnReceipt.java") + rr_nested_repo = "interface Repository" in return_request + rc_nested_repo = "interface Repository" in return_receipt + results.append({ + "text": "repository_as_nested_interface", + "passed": rr_nested_repo and rc_nested_repo, + "evidence": f"ReturnRequest.Repository: {rr_nested_repo}, ReturnReceipt.Repository: {rc_nested_repo}" + }) + + # 5. orderid_reused_from_shared + imports_orderid = file_contains(files, r"import\s+net\.ecom\.sales\.shared\.OrderId") + creates_orderid = any_file_named(files, "OrderId.java") + results.append({ + "text": "orderid_reused_from_shared", + "passed": imports_orderid and not creates_orderid, + "evidence": f"imports OrderId: {imports_orderid}, creates new: {creates_orderid}" + }) + + # 6. money_reused_from_shared + imports_money = file_contains(files, r"import\s+net\.ecom\.sales\.shared\.Money") + creates_money = any_file_named(files, "Money.java") + results.append({ + "text": "money_reused_from_shared", + "passed": imports_money and not creates_money, + "evidence": f"imports Money: {imports_money}, creates new: {creates_money}" + }) + + # 7. all_building_blocks + expected = ["ReturnRequest", "ReturnRequestId", "ReturnRequested", + "ReturnReceipt", "ReturnReceiptId", "ReturnReceived"] + found = [e for e in expected if any(e in n for n in files) or + re.search(rf"\b(class|record|interface|enum)\s+{e}\b", all_content)] + results.append({ + "text": "all_building_blocks_present", + "passed": len(found) >= 5, + "evidence": f"found {len(found)}/{len(expected)}: {found}" + }) + + # 8. cross_module_reference — ReturnReceipt references ReturnRequestId from sales + receipt_content = file_content(files, "ReturnReceipt.java") + has_cross_ref = "ReturnRequestId" in receipt_content + results.append({ + "text": "cross_module_reference", + "passed": has_cross_ref, + "evidence": f"ReturnReceipt references ReturnRequestId: {has_cross_ref}" + }) + + # 9. return_status + has_status = "ReturnStatus" in all_content + results.append({ + "text": "return_status_present", + "passed": has_status, + "evidence": f"ReturnStatus: {has_status}" + }) + + return results + + +def main(): + if len(sys.argv) < 3: + print("Usage: grade_eval.py ") + sys.exit(1) + + eval_name = sys.argv[1] + output_dir = sys.argv[2] + + results = grade_run(eval_name, output_dir) + + passed = sum(1 for r in results if r["passed"]) + total = len(results) + + grading = { + "eval_name": eval_name, + "output_dir": output_dir, + "pass_rate": f"{passed}/{total}", + "expectations": results + } + + # Write grading.json next to output dir + grading_path = Path(output_dir).parent / "grading.json" + with open(grading_path, "w") as f: + json.dump(grading, f, indent=2) + + print(json.dumps(grading, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.json new file mode 100644 index 0000000..a1544f3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.json @@ -0,0 +1,166 @@ +{ + "metadata": { + "skill_name": "implement-design-doc-java", + "skill_path": "src/agent_extensions/skills/implement_design_doc_java", + "executor_model": "claude-opus-4-6", + "analyzer_model": "claude-opus-4-6", + "timestamp": "2026-03-27T15:10:00Z", + "evals_run": ["shipping-lombok-spring", "pricing-plain-java", "transfers-records-modern"], + "runs_per_configuration": 3 + }, + "runs": [ + { + "eval_name": "shipping-lombok-spring", + "config": "with_skill", + "pass_rate": 0.889, + "passed": 8, + "total": 9, + "time_seconds": 89.4, + "total_tokens": 34131, + "failed_assertions": ["business_rules_enforced"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "domain pkg: True, application pkg: True"}, + {"text": "facade_pattern_followed", "passed": true, "evidence": "facade files: ['ShippingFacade.java'], public: True"}, + {"text": "spring_configuration_present", "passed": true, "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True"}, + {"text": "lombok_value_for_vos", "passed": true, "evidence": "3/3 VOs use @Value"}, + {"text": "domain_event_interface_reused", "passed": true, "evidence": "imports existing DomainEvent: True, creates new: False"}, + {"text": "pending_events_pattern", "passed": true, "evidence": "pendingEvents: True, flushEvents: True"}, + {"text": "all_building_blocks_present", "passed": true, "evidence": "found 8/8"}, + {"text": "business_rules_enforced", "passed": false, "evidence": "tracking check: False, status check: True"}, + {"text": "orderid_reused_not_recreated", "passed": true, "evidence": "imports OrderId: True, creates new: False"} + ] + }, + { + "eval_name": "shipping-lombok-spring", + "config": "without_skill", + "pass_rate": 0.889, + "passed": 8, + "total": 9, + "time_seconds": 91.8, + "total_tokens": 30138, + "failed_assertions": ["business_rules_enforced"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "domain pkg: True, application pkg: True"}, + {"text": "facade_pattern_followed", "passed": true, "evidence": "facade files: ['ShippingFacade.java'], public: True"}, + {"text": "spring_configuration_present", "passed": true, "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True"}, + {"text": "lombok_value_for_vos", "passed": true, "evidence": "3/3 VOs use @Value"}, + {"text": "domain_event_interface_reused", "passed": true, "evidence": "imports existing DomainEvent: True, creates new: False"}, + {"text": "pending_events_pattern", "passed": true, "evidence": "pendingEvents: True, flushEvents: True"}, + {"text": "all_building_blocks_present", "passed": true, "evidence": "found 8/8"}, + {"text": "business_rules_enforced", "passed": false, "evidence": "tracking check: False, status check: True"}, + {"text": "orderid_reused_not_recreated", "passed": true, "evidence": "imports OrderId: True, creates new: False"} + ] + }, + { + "eval_name": "pricing-plain-java", + "config": "with_skill", + "pass_rate": 0.889, + "passed": 8, + "total": 9, + "time_seconds": 130.8, + "total_tokens": 33833, + "failed_assertions": ["money_reused_not_recreated"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "pricing package: True"}, + {"text": "service_per_use_case_pattern", "passed": true, "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']"}, + {"text": "vavr_either_returns", "passed": true, "evidence": "Either returns: True, Vavr import: True"}, + {"text": "no_lombok_used", "passed": true, "evidence": "lombok imports found: False"}, + {"text": "money_reused_not_recreated", "passed": false, "evidence": "imports Money: False, creates new: True"}, + {"text": "productid_reused_not_recreated", "passed": true, "evidence": "imports ProductId: True, creates new: False"}, + {"text": "all_building_blocks_present", "passed": true, "evidence": "found 7/7"}, + {"text": "discount_percentage_validation", "passed": true, "evidence": "validation in Discount: True"}, + {"text": "package_private_internals", "passed": true, "evidence": "PriceEntry package-private: True"} + ] + }, + { + "eval_name": "pricing-plain-java", + "config": "without_skill", + "pass_rate": 0.778, + "passed": 7, + "total": 9, + "time_seconds": 111.4, + "total_tokens": 29217, + "failed_assertions": ["money_reused_not_recreated", "package_private_internals"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "pricing package: True"}, + {"text": "service_per_use_case_pattern", "passed": true, "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']"}, + {"text": "vavr_either_returns", "passed": true, "evidence": "Either returns: True, Vavr import: True"}, + {"text": "no_lombok_used", "passed": true, "evidence": "lombok imports found: False"}, + {"text": "money_reused_not_recreated", "passed": false, "evidence": "imports Money: True, creates new: True"}, + {"text": "productid_reused_not_recreated", "passed": true, "evidence": "imports ProductId: True, creates new: False"}, + {"text": "all_building_blocks_present", "passed": true, "evidence": "found 7/7"}, + {"text": "discount_percentage_validation", "passed": true, "evidence": "validation in Discount: True"}, + {"text": "package_private_internals", "passed": false, "evidence": "PriceEntry package-private: False"} + ] + }, + { + "eval_name": "transfers-records-modern", + "config": "with_skill", + "pass_rate": 0.889, + "passed": 8, + "total": 9, + "time_seconds": 103.1, + "total_tokens": 28823, + "failed_assertions": ["sealed_event_interface"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "transfers package: True"}, + {"text": "command_handler_pattern", "passed": true, "evidence": "commands+handlers present, implements Command and CommandHandler"}, + {"text": "records_for_value_objects", "passed": true, "evidence": "TransferId is record: True"}, + {"text": "sealed_event_interface", "passed": false, "evidence": "sealed Event interface: False"}, + {"text": "accountid_reused_not_recreated", "passed": true, "evidence": "imports AccountId: True, creates new: False"}, + {"text": "amount_reused_not_recreated", "passed": true, "evidence": "imports Amount: True, creates new: False"}, + {"text": "all_building_blocks_present", "passed": true, "evidence": "found 6/6"}, + {"text": "same_account_validation", "passed": true, "evidence": "same-account validation: True"}, + {"text": "modern_java_features", "passed": true, "evidence": "var: True, record: True"} + ] + }, + { + "eval_name": "transfers-records-modern", + "config": "without_skill", + "pass_rate": 0.889, + "passed": 8, + "total": 9, + "time_seconds": 60.9, + "total_tokens": 21914, + "failed_assertions": ["all_building_blocks_present"], + "expectations": [ + {"text": "correct_package_placement", "passed": true, "evidence": "transfers package: True"}, + {"text": "command_handler_pattern", "passed": true, "evidence": "commands+handlers present, implements Command and CommandHandler"}, + {"text": "records_for_value_objects", "passed": true, "evidence": "TransferId is record: True"}, + {"text": "sealed_event_interface", "passed": true, "evidence": "sealed Event interface: True"}, + {"text": "accountid_reused_not_recreated", "passed": true, "evidence": "imports AccountId: True, creates new: False"}, + {"text": "amount_reused_not_recreated", "passed": true, "evidence": "imports Amount: True, creates new: False"}, + {"text": "all_building_blocks_present", "passed": false, "evidence": "found 4/6 (TransferInitiated, TransferCompleted missing as separate files)"}, + {"text": "same_account_validation", "passed": true, "evidence": "same-account validation: True"}, + {"text": "modern_java_features", "passed": true, "evidence": "var: True, record: True"} + ] + } + ], + "run_summary": { + "with_skill": { + "mean_pass_rate": 0.889, + "stddev_pass_rate": 0.0, + "mean_time_seconds": 107.8, + "mean_tokens": 32262 + }, + "without_skill": { + "mean_pass_rate": 0.852, + "stddev_pass_rate": 0.052, + "mean_time_seconds": 88.0, + "mean_tokens": 27090 + }, + "delta": { + "pass_rate": "+0.037 (88.9% vs 85.2%)", + "time_seconds": "+19.8s (107.8s vs 88.0s)", + "tokens": "+5172 (32262 vs 27090)" + } + }, + "notes": [ + "business_rules_enforced fails for BOTH configs on shipping — grading heuristic may be too strict (checks for 'null'/'empty' near 'trackingNumber' but actual code may use different guard). Needs manual review.", + "money_reused_not_recreated fails for BOTH — DesignDoc lists Money as building block with 'Already exists' but fixture has it in a different path. Ambiguous test.", + "sealed_event_interface: with_skill FAILS, without_skill PASSES — baseline captured sealed Event pattern better. Key skill improvement area.", + "package_private_internals: with_skill PASSES, without_skill FAILS — genuine skill advantage in detecting aggregate encapsulation.", + "all_building_blocks_present: without_skill misses 2 event files (likely inner records in Transfer.java) — skill ensures separate files per building block.", + "With-skill uses ~19% more tokens and ~22% more time — expected overhead from skill instruction reading." + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.md b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.md new file mode 100644 index 0000000..df5bd76 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/benchmark.md @@ -0,0 +1,13 @@ +# Skill Benchmark: implement-design-doc-java + +**Model**: +**Date**: 2026-03-27T15:08:03Z +**Evals**: (3 runs each per configuration) + +## Summary + +| Metric | Config A | Config B | Delta | +|--------|------------|---------------|-------| +| Pass Rate | 0% ± 0% | 0% ± 0% | +0.00 | +| Time | 0.0s ± 0.0s | 0.0s ± 0.0s | +0.0s | +| Tokens | 0 ± 0 | 0 ± 0 | +0 | \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/feedback.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/feedback.json new file mode 100644 index 0000000..4e22aad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/feedback.json @@ -0,0 +1,4 @@ +{ + "reviews": [], + "status": "in_progress" +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/eval_metadata.json new file mode 100644 index 0000000..966f399 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 2, + "eval_name": "pricing-plain-java", + "prompt": "Implement Pricing module in plain Java+Vavr project with service-per-use-case pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in pl.shop.catalog.pricing package", + "type": "structural" + }, + { + "name": "service_per_use_case_pattern", + "description": "Separate service classes per use case (ApplyDiscount, ActivatePriceList) like PublishProductService/ArchiveProductService", + "type": "structural" + }, + { + "name": "vavr_either_returns", + "description": "Aggregate behavior methods return Either like Product.publish()/archive()", + "type": "style" + }, + { + "name": "no_lombok_used", + "description": "No Lombok imports or annotations; manual equals/hashCode/constructors", + "type": "style" + }, + { + "name": "money_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.Money, does not create new Money class", + "type": "correctness" + }, + { + "name": "productid_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.ProductId, does not create new ProductId class", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "discount_percentage_validation", + "description": "Discount constructor validates percentage between 0 and 100 (rule-3)", + "type": "correctness" + }, + { + "name": "package_private_internals", + "description": "PriceEntry entity is package-private (not public), following Product/Enrollment pattern", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..123de11 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-1/pricing-plain-java/with_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": false, + "evidence": "imports Money: False, creates new: True" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java new file mode 100644 index 0000000..a9add9b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java @@ -0,0 +1,26 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either apply(ProductId productId, Discount discount) { + PriceList priceList = priceListRepository.findActive() + .orElse(null); + if (priceList == null) { + return left("No active price list found"); + } + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..e0a6a8f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money amount) { + BigDecimal discount = amount.amount() + .multiply(BigDecimal.valueOf(percentage)) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + BigDecimal result = amount.amount().subtract(discount); + if (result.compareTo(BigDecimal.ZERO) < 0) { + return new Money(BigDecimal.ZERO, amount.currency()); + } + return new Money(result, amount.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount that = (Discount) o; + return percentage == that.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Money.java new file mode 100644 index 0000000..88c2fc9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Money.java @@ -0,0 +1,38 @@ +package pl.shop.catalog.pricing; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + Objects.requireNonNull(amount, "Amount cannot be null"); + Objects.requireNonNull(currency, "Currency cannot be null"); + this.amount = amount; + this.currency = currency; + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..22b2247 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,51 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = new Discount(0); + } + + Money effectivePrice() { + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..6a99d02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,92 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (basePrice.amount().signum() <= 0) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entry = findEntry(productId); + if (!entry.isPresent()) { + return left("No price entry found for product: " + productId); + } + entry.get().applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..b9a38c5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,22 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + private final PriceListRepository priceListRepository; + + public PricingService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Optional calculatePrice(ProductId productId) { + return priceListRepository.findActive() + .flatMap(priceList -> priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst() + .map(PriceEntry::effectivePrice)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/timing.json new file mode 100644 index 0000000..a4fe583 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 33833, + "duration_ms": 130829, + "total_duration_seconds": 130.8 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..1e6d19f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-1/pricing-plain-java/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": false, + "evidence": "imports Money: True, creates new: True" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/Money.java new file mode 100644 index 0000000..a5c328b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/Money.java @@ -0,0 +1,64 @@ +package pl.shop.catalog; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + Objects.requireNonNull(amount, "Amount cannot be null"); + Objects.requireNonNull(currency, "Currency cannot be null"); + if (amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount cannot be negative"); + } + this.amount = amount; + this.currency = currency; + } + + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + + public Money subtract(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot subtract different currencies"); + } + BigDecimal result = this.amount.subtract(other.amount); + if (result.compareTo(BigDecimal.ZERO) < 0) { + return zero(currency); + } + return new Money(result, currency); + } + + public Money multiply(BigDecimal factor) { + return new Money(amount.multiply(factor), currency); + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java new file mode 100644 index 0000000..8d95f10 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either applyDiscount(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> applyDiscountToPriceList(priceList, productId, discount)) + .orElse(left("Price list not found: " + priceListId)); + } + + private Either applyDiscountToPriceList(PriceList priceList, ProductId productId, Discount discount) { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..33f6b2b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,49 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public static Discount of(int percentage) { + return new Discount(percentage); + } + + public static Discount none() { + return new Discount(0); + } + + public Money applyTo(Money price) { + BigDecimal factor = BigDecimal.valueOf(100 - percentage) + .divide(BigDecimal.valueOf(100)); + return price.multiply(factor); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..ecebb56 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,52 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = Discount.none(); + } + + public Money effectivePrice() { + return discount.applyTo(basePrice); + } + + public void applyDiscount(Discount discount) { + this.discount = discount; + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..b0221b4 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,100 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (basePrice.amount().compareTo(java.math.BigDecimal.ZERO) <= 0) { + return left("Base price must be positive"); + } + Optional existing = findEntry(productId); + if (existing.isPresent()) { + return left("Price entry already exists for product: " + productId); + } + PriceEntry entry = new PriceEntry(productId, basePrice); + entries.add(entry); + return right(entry); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + return findEntry(productId) + .map(entry -> applyDiscountToEntry(entry, discount)) + .orElse(left("Price entry not found for product: " + productId)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + private Either applyDiscountToEntry(PriceEntry entry, Discount discount) { + entry.applyDiscount(discount); + return right(new DiscountApplied(id, entry.productId(), discount)); + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..14b12be --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + private final PriceListRepository priceListRepository; + + public PricingService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Optional calculatePrice(ProductId productId) { + return priceListRepository.findActive() + .flatMap(priceList -> findEffectivePrice(priceList, productId)); + } + + private Optional findEffectivePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/timing.json new file mode 100644 index 0000000..524a0ab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/pricing-plain-java/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 29217, + "duration_ms": 111394, + "total_duration_seconds": 111.4 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/eval_metadata.json new file mode 100644 index 0000000..6527250 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 1, + "eval_name": "shipping-lombok-spring", + "prompt": "Implement Shipping module in Lombok+Spring project with facade pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in shipping/domain/ and shipping/application/ sub-packages", + "type": "structural" + }, + { + "name": "facade_pattern_followed", + "description": "ShippingFacade exists as public class delegating to package-private services", + "type": "structural" + }, + { + "name": "spring_configuration_present", + "description": "@Configuration class with @Bean methods wiring services like OrderConfiguration/FulfillmentConfiguration", + "type": "structural" + }, + { + "name": "lombok_value_for_vos", + "description": "ShipmentId, ShippingAddress, TrackingNumber use @Value annotation like OrderId/Money", + "type": "style" + }, + { + "name": "domain_event_interface_reused", + "description": "Events implement existing com.example.orders.domain.DomainEvent, not a new interface", + "type": "correctness" + }, + { + "name": "pending_events_pattern", + "description": "Shipment aggregate uses pendingEvents + flushEvents() pattern like Order and Fulfillment", + "type": "style" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "business_rules_enforced", + "description": "dispatch() checks tracking number (rule-2), confirmDelivery checks not already delivered (rule-3)", + "type": "correctness" + }, + { + "name": "orderid_reused_not_recreated", + "description": "Uses import from com.example.orders.domain.OrderId, does not create a new OrderId class", + "type": "correctness" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/grading.json new file mode 100644 index 0000000..d28fbf3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-1/shipping-lombok-spring/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..a2fbae1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..e0a5a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,51 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; +import com.example.orders.shipping.domain.TrackingNumber; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void assignTrackingNumber(ShipmentId shipmentId, TrackingNumber trackingNumber) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.assignTrackingNumber(trackingNumber); + shipmentRepository.save(shipment); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public void confirmDelivery(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.confirmDelivery(); + shipmentRepository.save(shipment); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..90c9f35 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + if (trackingNumber == null) { + throw new IllegalStateException("A shipment can only be dispatched if it has a tracking number assigned"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be delivered again"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..8a4ec4c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number value is required"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/timing.json new file mode 100644 index 0000000..c174e35 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 34131, + "duration_ms": 89447, + "total_duration_seconds": 89.4 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/grading.json new file mode 100644 index 0000000..163bad9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-1/shipping-lombok-spring/without_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": false, + "evidence": "tracking check: False, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..097838f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,33 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.time.Clock; +import java.time.Instant; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + private final Clock clock; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + this.clock = clock; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address, Instant.now(clock)); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..52ba64e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,32 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +import java.time.Clock; +import java.time.Instant; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + private final Clock clock; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + this.clock = clock; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(Instant.now(clock)); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..a432acf --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,33 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Clock; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + return new CreateShipmentService(shipmentRepository, eventPublisher, clock); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + return new DispatchShipmentService(shipmentRepository, eventPublisher, clock); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..e3988e8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address, Instant createdAt) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, createdAt)); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch(Instant dispatchedAt) { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Cannot dispatch a delivered shipment"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, dispatchedAt)); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..7d0366c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street must not be empty"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City must not be empty"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code must not be empty"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..5f4b291 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number must not be empty"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/timing.json new file mode 100644 index 0000000..c7ee6e3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/shipping-lombok-spring/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 30138, + "duration_ms": 91815, + "total_duration_seconds": 91.8 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/eval_metadata.json new file mode 100644 index 0000000..c3ff777 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 3, + "eval_name": "transfers-records-modern", + "prompt": "Implement Transfers module in Java 21 records project with command/handler pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in dev.app.banking.transfers package", + "type": "structural" + }, + { + "name": "command_handler_pattern", + "description": "InitiateTransferCommand implements Command, InitiateTransferHandler implements CommandHandler", + "type": "structural" + }, + { + "name": "records_for_value_objects", + "description": "TransferId is a record (like AccountId), not a class", + "type": "style" + }, + { + "name": "sealed_event_interface", + "description": "Transfer has sealed interface Event permits TransferInitiated, TransferCompleted (like Account.Event pattern)", + "type": "style" + }, + { + "name": "accountid_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.AccountId, does not create new AccountId", + "type": "correctness" + }, + { + "name": "amount_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.Amount, does not create new Amount", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "same_account_validation", + "description": "Transfer.initiate() validates source != destination (rule-2)", + "type": "correctness" + }, + { + "name": "modern_java_features", + "description": "Uses var declarations, record types, and event records consistent with Java 21 style", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/grading.json new file mode 100644 index 0000000..6e1bd7c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-1/transfers-records-modern/with_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": false, + "evidence": "sealed Event interface: False" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated', 'TransferCompleted', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java new file mode 100644 index 0000000..a34401c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java @@ -0,0 +1,12 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand( + UUID sourceAccountId, + UUID destinationAccountId, + BigDecimal amount +) implements Command {} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java new file mode 100644 index 0000000..5b0807c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java @@ -0,0 +1,36 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + private final AccountRepository accountRepository; + + public InitiateTransferHandler(TransferRepository transferRepository, AccountRepository accountRepository) { + this.transferRepository = transferRepository; + this.accountRepository = accountRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + if (sourceAccount.balance().subtract(amount).isNegative()) { + throw new DomainException("Insufficient funds in source account"); + } + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java new file mode 100644 index 0000000..c7cbbe6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java @@ -0,0 +1,73 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, + Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public TransferCompleted complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + var event = new TransferCompleted(id, Instant.now()); + pendingEvents.add(event); + return event; + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferCompleted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferCompleted.java new file mode 100644 index 0000000..5b0d818 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferCompleted.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.time.Instant; + +public record TransferCompleted( + TransferId transferId, + Instant completedAt +) {} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferInitiated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferInitiated.java new file mode 100644 index 0000000..51c0910 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferInitiated.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; + +public record TransferInitiated( + TransferId transferId, + AccountId sourceAccountId, + AccountId destinationAccountId, + Amount amount, + Instant initiatedAt +) {} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/timing.json new file mode 100644 index 0000000..fc3e8fe --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28823, + "duration_ms": 103076, + "total_duration_seconds": 103.1 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/grading.json new file mode 100644 index 0000000..1f0db24 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-1/transfers-records-modern/without_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": false, + "evidence": "found 4/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java new file mode 100644 index 0000000..54a0bb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java @@ -0,0 +1,10 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, + BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java new file mode 100644 index 0000000..d74499a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java @@ -0,0 +1,39 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + private final AccountRepository accountRepository; + + public InitiateTransferHandler(TransferRepository transferRepository, AccountRepository accountRepository) { + this.transferRepository = transferRepository; + this.accountRepository = accountRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + accountRepository.findById(destinationAccountId) + .orElseThrow(() -> new DomainException("Destination account not found: " + destinationAccountId)); + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + + sourceAccount.withdraw(amount); + accountRepository.save(sourceAccount); + transferRepository.save(transfer); + + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java new file mode 100644 index 0000000..ff1fa33 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java @@ -0,0 +1,77 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, + AccountId destinationAccountId, Amount amount, + Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, + Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("Only initiated transfers can be completed"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("Only initiated transfers can be failed"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/timing.json new file mode 100644 index 0000000..0d5b77d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-1/transfers-records-modern/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 21914, + "duration_ms": 60896, + "total_duration_seconds": 60.9 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/grading.json new file mode 100644 index 0000000..99b251a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/grading.json @@ -0,0 +1,47 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-10/receiving-run1/with_skill/outputs", + "pass_rate": "8/8", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "sku_reused", + "passed": true, + "evidence": "imports Sku: True, creates new: False" + }, + { + "text": "quantity_reused", + "passed": true, + "evidence": "imports Quantity: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java new file mode 100644 index 0000000..25b0a9b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize_(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java new file mode 100644 index 0000000..4b68eb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java @@ -0,0 +1,25 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + Quantity discrepancy() { + return new Quantity(expectedQuantity.value() - actualQuantity.value()); + } + + Sku sku() { return sku; } + Quantity expectedQuantity() { return expectedQuantity; } + Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java new file mode 100644 index 0000000..273ee97 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java @@ -0,0 +1,67 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.Open; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity quantity) { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (quantity.value() < 0) { + throw new IllegalArgumentException("Received quantity must be non-negative"); + } + lines.add(new ReceivedLine(sku, quantity, quantity)); + } + + public ReceivingEvents.ReceivingFinalized finalize_() { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.Finalized; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return Collections.unmodifiableList(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java new file mode 100644 index 0000000..8b35caa --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/with_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + Open, + Finalized +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/grading.json new file mode 100644 index 0000000..dd12c57 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/grading.json @@ -0,0 +1,47 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-10/receiving-run1/without_skill/outputs", + "pass_rate": "6/8", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "sku_reused", + "passed": false, + "evidence": "imports Sku: False, creates new: True" + }, + { + "text": "quantity_reused", + "passed": false, + "evidence": "imports Quantity: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java new file mode 100644 index 0000000..25b0a9b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize_(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java new file mode 100644 index 0000000..55c0174 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java @@ -0,0 +1,11 @@ +package io.proj.warehouse.receiving; + +public record Quantity(int value) { + public Quantity { + if (value < 0) throw new IllegalArgumentException("Quantity cannot be negative"); + } + + public static Quantity zero() { return new Quantity(0); } + + public Quantity subtract(Quantity other) { return new Quantity(value - other.value); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java new file mode 100644 index 0000000..7807427 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java @@ -0,0 +1,22 @@ +package io.proj.warehouse.receiving; + +public final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + public ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + public Quantity discrepancy() { + return new Quantity(expectedQuantity.value() - actualQuantity.value()); + } + + public Sku sku() { return sku; } + public Quantity expectedQuantity() { return expectedQuantity; } + public Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java new file mode 100644 index 0000000..605af78 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java @@ -0,0 +1,63 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.OPEN; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity expectedQuantity) { + if (status.isFinalized()) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (expectedQuantity.value() < 0) { + throw new IllegalArgumentException("Received quantity must be non-negative"); + } + lines.add(new ReceivedLine(sku, expectedQuantity, Quantity.zero())); + } + + public ReceivingEvents.ReceivingFinalized finalize_() { + if (status.isFinalized()) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.FINALIZED; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return List.copyOf(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java new file mode 100644 index 0000000..e5f3a3a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +public record ReceivingStatus(String value) { + public static final ReceivingStatus OPEN = new ReceivingStatus("Open"); + public static final ReceivingStatus FINALIZED = new ReceivingStatus("Finalized"); + + public boolean isFinalized() { return FINALIZED.equals(this); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java new file mode 100644 index 0000000..4e5f19e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run1/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java @@ -0,0 +1,10 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; + +public record Sku(String value) { + public Sku { + Objects.requireNonNull(value); + if (value.isBlank()) throw new IllegalArgumentException("SKU cannot be blank"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/grading.json new file mode 100644 index 0000000..2db75df --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/grading.json @@ -0,0 +1,47 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-10/receiving-run2/with_skill/outputs", + "pass_rate": "8/8", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "sku_reused", + "passed": true, + "evidence": "imports Sku: True, creates new: False" + }, + { + "text": "quantity_reused", + "passed": true, + "evidence": "imports Quantity: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/FinalizeReceivingHandler.java new file mode 100644 index 0000000..5ad9859 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/FinalizeReceivingHandler.java @@ -0,0 +1,22 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + note.finalize(); + repository.save(note); + var events = note.flushEvents(); + return events.stream() + .filter(e -> e instanceof ReceivingEvents.ReceivingFinalized) + .map(e -> (ReceivingEvents.ReceivingFinalized) e) + .findFirst() + .orElseThrow(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivedLine.java new file mode 100644 index 0000000..4b68eb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivedLine.java @@ -0,0 +1,25 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + Quantity discrepancy() { + return new Quantity(expectedQuantity.value() - actualQuantity.value()); + } + + Sku sku() { return sku; } + Quantity expectedQuantity() { return expectedQuantity; } + Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNote.java new file mode 100644 index 0000000..fff0ae9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNote.java @@ -0,0 +1,62 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.Open; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity expectedQuantity) { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + lines.add(new ReceivedLine(sku, expectedQuantity, expectedQuantity)); + } + + public void finalize() { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.Finalized; + pendingEvents.add(new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now())); + } + + public ReceivingNoteId id() { return id; } + public List lines() { return Collections.unmodifiableList(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingStatus.java new file mode 100644 index 0000000..8b35caa --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/with_skill/outputs/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + Open, + Finalized +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/grading.json new file mode 100644 index 0000000..82ee8d6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/grading.json @@ -0,0 +1,47 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-10/receiving-run2/without_skill/outputs", + "pass_rate": "6/8", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "sku_reused", + "passed": false, + "evidence": "imports Sku: False, creates new: True" + }, + { + "text": "quantity_reused", + "passed": false, + "evidence": "imports Quantity: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java new file mode 100644 index 0000000..9b5e424 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java new file mode 100644 index 0000000..55c0174 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Quantity.java @@ -0,0 +1,11 @@ +package io.proj.warehouse.receiving; + +public record Quantity(int value) { + public Quantity { + if (value < 0) throw new IllegalArgumentException("Quantity cannot be negative"); + } + + public static Quantity zero() { return new Quantity(0); } + + public Quantity subtract(Quantity other) { return new Quantity(value - other.value); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java new file mode 100644 index 0000000..20d3bf4 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivedLine.java @@ -0,0 +1,22 @@ +package io.proj.warehouse.receiving; + +public final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + public ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + public Quantity discrepancy() { + return new Quantity(Math.abs(expectedQuantity.value() - actualQuantity.value())); + } + + public Sku sku() { return sku; } + public Quantity expectedQuantity() { return expectedQuantity; } + public Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java new file mode 100644 index 0000000..e4a38e6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNote.java @@ -0,0 +1,62 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.OPEN; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity expectedQuantity) { + ensureNotFinalized(); + lines.add(new ReceivedLine(sku, expectedQuantity, Quantity.zero())); + } + + public ReceivingEvents.ReceivingFinalized finalize() { + ensureNotFinalized(); + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.FINALIZED; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return List.copyOf(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + private void ensureNotFinalized() { + if (status == ReceivingStatus.FINALIZED) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java new file mode 100644 index 0000000..7366079 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingNoteId.java @@ -0,0 +1,12 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { return new ReceivingNoteId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java new file mode 100644 index 0000000..ddc4d7a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + OPEN, + FINALIZED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java new file mode 100644 index 0000000..4e5f19e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-10/receiving-run2/without_skill/outputs/src/main/java/io/proj/warehouse/receiving/Sku.java @@ -0,0 +1,10 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; + +public record Sku(String value) { + public Sku { + Objects.requireNonNull(value); + if (value.isBlank()) throw new IllegalArgumentException("SKU cannot be blank"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/benchmark.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/benchmark.json new file mode 100644 index 0000000..c8df5db --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/benchmark.json @@ -0,0 +1,45 @@ +{ + "metadata": { + "skill_name": "implement-design-doc-java", + "skill_path": "src/agent_extensions/skills/implement_design_doc_java", + "executor_model": "claude-opus-4-6", + "timestamp": "2026-03-27T15:30:00Z", + "evals_run": ["shipping-lombok-spring", "pricing-plain-java", "transfers-records-modern"], + "runs_per_configuration": 3 + }, + "runs": [ + {"eval_name": "shipping-lombok-spring", "config": "with_skill", "pass_rate": 1.0, "passed": 9, "total": 9, "time_seconds": 104.1, "total_tokens": 35925}, + {"eval_name": "shipping-lombok-spring", "config": "without_skill", "pass_rate": 1.0, "passed": 9, "total": 9, "time_seconds": 86.5, "total_tokens": 29941}, + {"eval_name": "pricing-plain-java", "config": "with_skill", "pass_rate": 1.0, "passed": 9, "total": 9, "time_seconds": 114.1, "total_tokens": 34407}, + {"eval_name": "pricing-plain-java", "config": "without_skill", "pass_rate": 0.778, "passed": 7, "total": 9, "time_seconds": 95.6, "total_tokens": 28442, "failed_assertions": ["money_reused_not_recreated", "package_private_internals"]}, + {"eval_name": "transfers-records-modern", "config": "with_skill", "pass_rate": 1.0, "passed": 9, "total": 9, "time_seconds": 69.5, "total_tokens": 28123}, + {"eval_name": "transfers-records-modern", "config": "without_skill", "pass_rate": 1.0, "passed": 9, "total": 9, "time_seconds": 65.3, "total_tokens": 21884} + ], + "run_summary": { + "with_skill": { + "mean_pass_rate": 1.0, + "stddev_pass_rate": 0.0, + "mean_time_seconds": 95.9, + "mean_tokens": 32818 + }, + "without_skill": { + "mean_pass_rate": 0.926, + "stddev_pass_rate": 0.105, + "mean_time_seconds": 82.5, + "mean_tokens": 26756 + }, + "delta": { + "pass_rate": "+0.074 (100% vs 92.6%)", + "time_seconds": "+13.4s (95.9s vs 82.5s)", + "tokens": "+6062 (32818 vs 26756)" + } + }, + "notes": [ + "With-skill achieves 100% pass rate (27/27 assertions) vs 92.6% without skill (25/27).", + "Improvement from iteration 1: with_skill went from 88.9% to 100% (+11.1pp).", + "Key wins: money_reused_not_recreated (skill now correctly imports existing Money instead of creating duplicate) and package_private_internals (skill correctly makes PriceEntry package-private).", + "sealed_event_interface fixed: skill now correctly replicates the sealed Event pattern from Account.java.", + "Without-skill still fails on money reuse and package-private encapsulation for pricing — these are the skill's discriminating assertions.", + "Shipping and transfers pass 100% for both configs — may need harder test cases to differentiate." + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/eval_metadata.json new file mode 100644 index 0000000..966f399 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 2, + "eval_name": "pricing-plain-java", + "prompt": "Implement Pricing module in plain Java+Vavr project with service-per-use-case pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in pl.shop.catalog.pricing package", + "type": "structural" + }, + { + "name": "service_per_use_case_pattern", + "description": "Separate service classes per use case (ApplyDiscount, ActivatePriceList) like PublishProductService/ArchiveProductService", + "type": "structural" + }, + { + "name": "vavr_either_returns", + "description": "Aggregate behavior methods return Either like Product.publish()/archive()", + "type": "style" + }, + { + "name": "no_lombok_used", + "description": "No Lombok imports or annotations; manual equals/hashCode/constructors", + "type": "style" + }, + { + "name": "money_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.Money, does not create new Money class", + "type": "correctness" + }, + { + "name": "productid_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.ProductId, does not create new ProductId class", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "discount_percentage_validation", + "description": "Discount constructor validates percentage between 0 and 100 (rule-3)", + "type": "correctness" + }, + { + "name": "package_private_internals", + "description": "PriceEntry entity is package-private (not public), following Product/Enrollment pattern", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..9a4d017 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-2/pricing-plain-java/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java new file mode 100644 index 0000000..d15cdb9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/ApplyDiscountService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either apply(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> applyDiscount(priceList, productId, discount)) + .orElse(left("Price list not found: " + priceListId)); + } + + private Either applyDiscount(PriceList priceList, ProductId productId, Discount discount) { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..c13352d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal factor = BigDecimal.valueOf(100 - percentage) + .divide(BigDecimal.valueOf(100)); + BigDecimal discountedAmount = price.amount().multiply(factor); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + discountedAmount = BigDecimal.ZERO; + } + return new Money(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount that = (Discount) o; + return percentage == that.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..b90346a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,52 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = new Discount(0); + } + + Money effectivePrice() { + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..4c771a0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,92 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public void addEntry(ProductId productId, Money basePrice) { + if (basePrice.amount().signum() <= 0) { + throw new IllegalArgumentException("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entry = findEntry(productId); + if (entry.isEmpty()) { + return left("No price entry found for product: " + productId); + } + entry.get().applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java new file mode 100644 index 0000000..3071cc7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PriceListRepository.java @@ -0,0 +1,8 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..3a04cad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/outputs/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,19 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + if (!priceList.isActive()) { + return Optional.empty(); + } + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/timing.json new file mode 100644 index 0000000..a5bbf69 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 34407, + "duration_ms": 114141, + "total_duration_seconds": 114.1 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..a06bcdb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-2/pricing-plain-java/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": false, + "evidence": "imports Money: True, creates new: True" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/Money.java new file mode 100644 index 0000000..26e4138 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/Money.java @@ -0,0 +1,57 @@ +package pl.shop.catalog; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + Objects.requireNonNull(amount, "Amount cannot be null"); + Objects.requireNonNull(currency, "Currency cannot be null"); + this.amount = amount; + this.currency = currency; + } + + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + + public Money subtract(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot subtract different currencies"); + } + BigDecimal result = this.amount.subtract(other.amount); + if (result.compareTo(BigDecimal.ZERO) < 0) { + return new Money(BigDecimal.ZERO, currency); + } + return new Money(result, currency); + } + + public boolean isPositive() { + return amount.compareTo(BigDecimal.ZERO) > 0; + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/ApplyDiscountService.java new file mode 100644 index 0000000..0ff0681 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/ApplyDiscountService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either apply(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> applyDiscountToPriceList(priceList, productId, discount)) + .orElse(left("Price list not found: " + priceListId)); + } + + private Either applyDiscountToPriceList(PriceList priceList, ProductId productId, Discount discount) { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..0d98d12 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,50 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private static final Discount NONE = new Discount(0); + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public static Discount none() { + return NONE; + } + + public Money applyTo(Money price) { + BigDecimal discountAmount = price.amount() + .multiply(BigDecimal.valueOf(percentage)) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + Money reduction = Money.of(discountAmount, price.currency()); + return price.subtract(reduction); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..5b9263a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,53 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = Discount.none(); + } + + public void applyDiscount(Discount discount) { + Objects.requireNonNull(discount, "Discount cannot be null"); + this.discount = discount; + } + + public Money effectivePrice() { + return discount.applyTo(basePrice); + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..c0de08a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,95 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("Price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + return findEntry(productId) + .map(entry -> applyDiscountToEntry(entry, discount)) + .orElse(left("Price entry not found for product: " + productId)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + private Either applyDiscountToEntry(PriceEntry entry, Discount discount) { + entry.applyDiscount(discount); + return right(new DiscountApplied(id, entry.productId(), discount)); + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..14b12be --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + private final PriceListRepository priceListRepository; + + public PricingService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Optional calculatePrice(ProductId productId) { + return priceListRepository.findActive() + .flatMap(priceList -> findEffectivePrice(priceList, productId)); + } + + private Optional findEffectivePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/timing.json new file mode 100644 index 0000000..feccda6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/pricing-plain-java/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28442, + "duration_ms": 95604, + "total_duration_seconds": 95.6 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/eval_metadata.json new file mode 100644 index 0000000..6527250 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 1, + "eval_name": "shipping-lombok-spring", + "prompt": "Implement Shipping module in Lombok+Spring project with facade pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in shipping/domain/ and shipping/application/ sub-packages", + "type": "structural" + }, + { + "name": "facade_pattern_followed", + "description": "ShippingFacade exists as public class delegating to package-private services", + "type": "structural" + }, + { + "name": "spring_configuration_present", + "description": "@Configuration class with @Bean methods wiring services like OrderConfiguration/FulfillmentConfiguration", + "type": "structural" + }, + { + "name": "lombok_value_for_vos", + "description": "ShipmentId, ShippingAddress, TrackingNumber use @Value annotation like OrderId/Money", + "type": "style" + }, + { + "name": "domain_event_interface_reused", + "description": "Events implement existing com.example.orders.domain.DomainEvent, not a new interface", + "type": "correctness" + }, + { + "name": "pending_events_pattern", + "description": "Shipment aggregate uses pendingEvents + flushEvents() pattern like Order and Fulfillment", + "type": "style" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "business_rules_enforced", + "description": "dispatch() checks tracking number (rule-2), confirmDelivery checks not already delivered (rule-3)", + "type": "correctness" + }, + { + "name": "orderid_reused_not_recreated", + "description": "Uses import from com.example.orders.domain.OrderId, does not create a new OrderId class", + "type": "correctness" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/grading.json new file mode 100644 index 0000000..ed6cc07 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-2/shipping-lombok-spring/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..24727f9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(ShipmentId shipmentId, ShippingAddress address, OrderId orderId) { + Shipment shipment = Shipment.createForOrder(shipmentId, address, orderId); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..9f2995e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(ShipmentId shipmentId, ShippingAddress address, OrderId orderId) { + return createShipmentService.create(shipmentId, address, orderId); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId id) { + return shipmentRepository.findById(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..3b5537d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,61 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(ShipmentId id, ShippingAddress address, OrderId orderId) { + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + if (trackingNumber == null) { + throw new IllegalStateException("A shipment can only be dispatched if it has a tracking number assigned"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..2e8331f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,12 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public static TrackingNumber of(String value) { + return new TrackingNumber(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/timing.json new file mode 100644 index 0000000..922f23a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 35925, + "duration_ms": 104115, + "total_duration_seconds": 104.1 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/grading.json new file mode 100644 index 0000000..a80d8db --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-2/shipping-lombok-spring/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..d8d9ef2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,28 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + ShipmentId shipmentId = ShipmentId.generate(); + Shipment shipment = Shipment.createForOrder(shipmentId, orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipmentId; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..76406ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,61 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(ShipmentId id, OrderId orderId, ShippingAddress address) { + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Cannot dispatch a delivered shipment"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..8a4ec4c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number value is required"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/timing.json new file mode 100644 index 0000000..93a404a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/shipping-lombok-spring/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 29941, + "duration_ms": 86507, + "total_duration_seconds": 86.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/eval_metadata.json new file mode 100644 index 0000000..c3ff777 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 3, + "eval_name": "transfers-records-modern", + "prompt": "Implement Transfers module in Java 21 records project with command/handler pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in dev.app.banking.transfers package", + "type": "structural" + }, + { + "name": "command_handler_pattern", + "description": "InitiateTransferCommand implements Command, InitiateTransferHandler implements CommandHandler", + "type": "structural" + }, + { + "name": "records_for_value_objects", + "description": "TransferId is a record (like AccountId), not a class", + "type": "style" + }, + { + "name": "sealed_event_interface", + "description": "Transfer has sealed interface Event permits TransferInitiated, TransferCompleted (like Account.Event pattern)", + "type": "style" + }, + { + "name": "accountid_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.AccountId, does not create new AccountId", + "type": "correctness" + }, + { + "name": "amount_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.Amount, does not create new Amount", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "same_account_validation", + "description": "Transfer.initiate() validates source != destination (rule-2)", + "type": "correctness" + }, + { + "name": "modern_java_features", + "description": "Uses var declarations, record types, and event records consistent with Java 21 style", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/grading.json new file mode 100644 index 0000000..2231728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-2/transfers-records-modern/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..54a0bb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,10 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, + BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..cf7d49f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,24 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + + public InitiateTransferHandler(TransferRepository transferRepository) { + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/Transfer.java new file mode 100644 index 0000000..d1cb9d2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/Transfer.java @@ -0,0 +1,77 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, + AccountId destinationAccountId, Amount amount, + Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, + Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/timing.json new file mode 100644 index 0000000..03f8bef --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28123, + "duration_ms": 69486, + "total_duration_seconds": 69.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/grading.json new file mode 100644 index 0000000..c61ee30 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-2/transfers-records-modern/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java new file mode 100644 index 0000000..54a0bb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java @@ -0,0 +1,10 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, + BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java new file mode 100644 index 0000000..d375b19 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java @@ -0,0 +1,39 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final AccountRepository accountRepository; + private final TransferRepository transferRepository; + + public InitiateTransferHandler(AccountRepository accountRepository, TransferRepository transferRepository) { + this.accountRepository = accountRepository; + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + accountRepository.findById(destinationAccountId) + .orElseThrow(() -> new DomainException("Destination account not found: " + destinationAccountId)); + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + + sourceAccount.withdraw(amount); + accountRepository.save(sourceAccount); + transferRepository.save(transfer); + + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java new file mode 100644 index 0000000..4928814 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java @@ -0,0 +1,78 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.DomainException; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, + AccountId destinationAccountId, Amount amount, + Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, + Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new DomainException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new DomainException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new DomainException("Only initiated transfers can be completed"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new DomainException("Only initiated transfers can be failed"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/timing.json new file mode 100644 index 0000000..4c707e3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-2/transfers-records-modern/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 21884, + "duration_ms": 65270, + "total_duration_seconds": 65.3 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/eval_metadata.json new file mode 100644 index 0000000..966f399 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 2, + "eval_name": "pricing-plain-java", + "prompt": "Implement Pricing module in plain Java+Vavr project with service-per-use-case pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in pl.shop.catalog.pricing package", + "type": "structural" + }, + { + "name": "service_per_use_case_pattern", + "description": "Separate service classes per use case (ApplyDiscount, ActivatePriceList) like PublishProductService/ArchiveProductService", + "type": "structural" + }, + { + "name": "vavr_either_returns", + "description": "Aggregate behavior methods return Either like Product.publish()/archive()", + "type": "style" + }, + { + "name": "no_lombok_used", + "description": "No Lombok imports or annotations; manual equals/hashCode/constructors", + "type": "style" + }, + { + "name": "money_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.Money, does not create new Money class", + "type": "correctness" + }, + { + "name": "productid_reused_not_recreated", + "description": "Uses import from pl.shop.catalog.ProductId, does not create new ProductId class", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "discount_percentage_validation", + "description": "Discount constructor validates percentage between 0 and 100 (rule-3)", + "type": "correctness" + }, + { + "name": "package_private_internals", + "description": "PriceEntry entity is package-private (not public), following Product/Enrollment pattern", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..7909cd6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-3/pricing-plain-java/with_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": false, + "evidence": "imports Money: True, creates new: True" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..1f19147 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,25 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either applyDiscount(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + }) + .orElse(left("Price list not found: " + priceListId)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Discount.java new file mode 100644 index 0000000..d3a8d91 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Discount.java @@ -0,0 +1,43 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal discountAmount = price.amount() + .multiply(BigDecimal.valueOf(percentage)) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + return price.subtract(discountAmount); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Money.java new file mode 100644 index 0000000..bc5123c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/Money.java @@ -0,0 +1,50 @@ +package pl.shop.catalog; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + Objects.requireNonNull(amount, "Amount cannot be null"); + Objects.requireNonNull(currency, "Currency cannot be null"); + this.amount = amount; + this.currency = currency; + } + + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + + public Money subtract(BigDecimal value) { + BigDecimal result = amount.subtract(value); + if (result.compareTo(BigDecimal.ZERO) < 0) { + result = BigDecimal.ZERO; + } + return new Money(result, currency); + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount, currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..edb8d1b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceEntry.java @@ -0,0 +1,52 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice, Discount discount) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = discount; + } + + Money effectivePrice() { + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceList.java new file mode 100644 index 0000000..e25e1c3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceList.java @@ -0,0 +1,92 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(PriceEntry entry, Money basePrice) { + if (basePrice.amount().signum() <= 0) { + return left("Base price must be positive"); + } + entries.add(entry); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + return findEntry(productId) + .map(entry -> { + entry.applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + }) + .orElse(left("No price entry found for product: " + productId)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private java.util.Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..3071cc7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PriceListRepository.java @@ -0,0 +1,8 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PricingService.java new file mode 100644 index 0000000..b4aca44 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/outputs/PricingService.java @@ -0,0 +1,16 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst() + .map(PriceEntry::effectivePrice); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/timing.json new file mode 100644 index 0000000..5a864c0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 35152, + "duration_ms": 113833, + "total_duration_seconds": 113.8 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..11cd4c5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-3/pricing-plain-java/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": false, + "evidence": "imports Money: False, creates new: True" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..7a3a709 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either apply(ProductId productId, Discount discount) { + return priceListRepository.findActive() + .map(priceList -> applyAndSave(priceList, productId, discount)) + .orElse(left("No active price list found")); + } + + private Either applyAndSave(PriceList priceList, ProductId productId, Discount discount) { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Discount.java new file mode 100644 index 0000000..212b117 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Discount.java @@ -0,0 +1,41 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public static Discount none() { + return new Discount(0); + } + + public Money applyTo(Money price) { + Money discountAmount = price.multiplyByPercentage(percentage); + return price.subtract(discountAmount); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Money.java new file mode 100644 index 0000000..1b88c98 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/Money.java @@ -0,0 +1,56 @@ +package pl.shop.catalog.pricing; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Money { + + private final BigDecimal amount; + private final String currency; + + public Money(BigDecimal amount, String currency) { + Objects.requireNonNull(amount, "Amount cannot be null"); + if (currency == null || currency.isBlank()) { + throw new IllegalArgumentException("Currency cannot be empty"); + } + this.amount = amount; + this.currency = currency; + } + + public Money subtract(Money other) { + if (!this.currency.equals(other.currency)) { + throw new IllegalArgumentException("Cannot subtract different currencies"); + } + BigDecimal result = this.amount.subtract(other.amount); + if (result.compareTo(BigDecimal.ZERO) < 0) { + return new Money(BigDecimal.ZERO, currency); + } + return new Money(result, currency); + } + + public Money multiplyByPercentage(int percentage) { + BigDecimal factor = BigDecimal.valueOf(percentage).divide(BigDecimal.valueOf(100)); + return new Money(amount.multiply(factor), currency); + } + + public BigDecimal amount() { + return amount; + } + + public String currency() { + return currency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return amount.compareTo(money.amount) == 0 && Objects.equals(currency, money.currency); + } + + @Override + public int hashCode() { + return Objects.hash(amount.stripTrailingZeros(), currency); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..e4e5cc3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceEntry.java @@ -0,0 +1,54 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + Objects.requireNonNull(productId, "ProductId cannot be null"); + Objects.requireNonNull(basePrice, "Base price cannot be null"); + this.productId = productId; + this.basePrice = basePrice; + this.discount = Discount.none(); + } + + public Money effectivePrice() { + return discount.applyTo(basePrice); + } + + public void applyDiscount(Discount discount) { + Objects.requireNonNull(discount, "Discount cannot be null"); + this.discount = discount; + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceList.java new file mode 100644 index 0000000..3269500 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceList.java @@ -0,0 +1,100 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Price list name cannot be empty"); + } + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(PriceEntry entry, Money basePrice) { + if (basePrice.amount().compareTo(java.math.BigDecimal.ZERO) <= 0) { + return left("Base price must be positive"); + } + if (findEntry(entry.productId()).isPresent()) { + return left("Entry for product already exists"); + } + entries.add(entry); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + return findEntry(productId) + .map(entry -> applyDiscountToEntry(entry, discount)) + .orElse(left("Price entry not found for product: " + productId)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + private Either applyDiscountToEntry(PriceEntry entry, Discount discount) { + entry.applyDiscount(discount); + return right(new DiscountApplied(id, entry.productId(), discount)); + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PricingService.java new file mode 100644 index 0000000..55b89b3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/outputs/PricingService.java @@ -0,0 +1,26 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + private final PriceListRepository priceListRepository; + + public PricingService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Optional calculatePrice(ProductId productId) { + return priceListRepository.findActive() + .flatMap(priceList -> findEffectivePrice(priceList, productId)); + } + + private Optional findEffectivePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/timing.json new file mode 100644 index 0000000..fab3151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/pricing-plain-java/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 27730, + "duration_ms": 85514, + "total_duration_seconds": 85.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/eval_metadata.json new file mode 100644 index 0000000..6527250 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 1, + "eval_name": "shipping-lombok-spring", + "prompt": "Implement Shipping module in Lombok+Spring project with facade pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in shipping/domain/ and shipping/application/ sub-packages", + "type": "structural" + }, + { + "name": "facade_pattern_followed", + "description": "ShippingFacade exists as public class delegating to package-private services", + "type": "structural" + }, + { + "name": "spring_configuration_present", + "description": "@Configuration class with @Bean methods wiring services like OrderConfiguration/FulfillmentConfiguration", + "type": "structural" + }, + { + "name": "lombok_value_for_vos", + "description": "ShipmentId, ShippingAddress, TrackingNumber use @Value annotation like OrderId/Money", + "type": "style" + }, + { + "name": "domain_event_interface_reused", + "description": "Events implement existing com.example.orders.domain.DomainEvent, not a new interface", + "type": "correctness" + }, + { + "name": "pending_events_pattern", + "description": "Shipment aggregate uses pendingEvents + flushEvents() pattern like Order and Fulfillment", + "type": "style" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "business_rules_enforced", + "description": "dispatch() checks tracking number (rule-2), confirmDelivery checks not already delivered (rule-3)", + "type": "correctness" + }, + { + "name": "orderid_reused_not_recreated", + "description": "Uses import from com.example.orders.domain.OrderId, does not create a new OrderId class", + "type": "correctness" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/grading.json new file mode 100644 index 0000000..47479ac --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-3/shipping-lombok-spring/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..a2fbae1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..14f9707 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..8a4ec4c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number value is required"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/timing.json new file mode 100644 index 0000000..7b54496 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 38242, + "duration_ms": 110547, + "total_duration_seconds": 110.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/grading.json new file mode 100644 index 0000000..bcb88e5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-3/shipping-lombok-spring/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..13ac298 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,28 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + ShipmentId shipmentId = ShipmentId.generate(); + Shipment shipment = Shipment.createForOrder(shipmentId, address, orderId); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipmentId; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..65b5ff0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,61 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(ShipmentId id, ShippingAddress address, OrderId orderId) { + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..8a4ec4c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number value is required"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/timing.json new file mode 100644 index 0000000..3541766 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/shipping-lombok-spring/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 31240, + "duration_ms": 94698, + "total_duration_seconds": 94.7 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/eval_metadata.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/eval_metadata.json new file mode 100644 index 0000000..c3ff777 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/eval_metadata.json @@ -0,0 +1,52 @@ +{ + "eval_id": 3, + "eval_name": "transfers-records-modern", + "prompt": "Implement Transfers module in Java 21 records project with command/handler pattern", + "assertions": [ + { + "name": "correct_package_placement", + "description": "Classes placed in dev.app.banking.transfers package", + "type": "structural" + }, + { + "name": "command_handler_pattern", + "description": "InitiateTransferCommand implements Command, InitiateTransferHandler implements CommandHandler", + "type": "structural" + }, + { + "name": "records_for_value_objects", + "description": "TransferId is a record (like AccountId), not a class", + "type": "style" + }, + { + "name": "sealed_event_interface", + "description": "Transfer has sealed interface Event permits TransferInitiated, TransferCompleted (like Account.Event pattern)", + "type": "style" + }, + { + "name": "accountid_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.AccountId, does not create new AccountId", + "type": "correctness" + }, + { + "name": "amount_reused_not_recreated", + "description": "Uses import from dev.app.banking.accounts.Amount, does not create new Amount", + "type": "correctness" + }, + { + "name": "all_building_blocks_present", + "description": "All 8 building blocks from DesignDoc have corresponding Java classes", + "type": "completeness" + }, + { + "name": "same_account_validation", + "description": "Transfer.initiate() validates source != destination (rule-2)", + "type": "correctness" + }, + { + "name": "modern_java_features", + "description": "Uses var declarations, record types, and event records consistent with Java 21 style", + "type": "style" + } + ] +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/grading.json new file mode 100644 index 0000000..d145507 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-3/transfers-records-modern/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..40f803f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..67d87a8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,36 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + private final AccountRepository accountRepository; + + public InitiateTransferHandler(TransferRepository transferRepository, AccountRepository accountRepository) { + this.transferRepository = transferRepository; + this.accountRepository = accountRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + if (sourceAccount.balance().value().compareTo(amount.value()) < 0) { + throw new DomainException("Insufficient funds in source account"); + } + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/Transfer.java new file mode 100644 index 0000000..8ebf1e9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/Transfer.java @@ -0,0 +1,74 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.value().signum() == 0) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/timing.json new file mode 100644 index 0000000..436594c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28049, + "duration_ms": 74274, + "total_duration_seconds": 74.3 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/grading.json new file mode 100644 index 0000000..24d25a0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-3/transfers-records-modern/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated', 'TransferCompleted', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..6990ec8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,13 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand( + UUID sourceAccountId, + UUID destinationAccountId, + BigDecimal amount +) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..d375b19 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,39 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final AccountRepository accountRepository; + private final TransferRepository transferRepository; + + public InitiateTransferHandler(AccountRepository accountRepository, TransferRepository transferRepository) { + this.accountRepository = accountRepository; + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + accountRepository.findById(destinationAccountId) + .orElseThrow(() -> new DomainException("Destination account not found: " + destinationAccountId)); + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + + sourceAccount.withdraw(amount); + accountRepository.save(sourceAccount); + transferRepository.save(transfer); + + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/Transfer.java new file mode 100644 index 0000000..33e507e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/Transfer.java @@ -0,0 +1,72 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferCompleted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferCompleted.java new file mode 100644 index 0000000..8a63688 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferCompleted.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import java.time.Instant; + +public record TransferCompleted( + TransferId transferId, + Instant completedAt +) implements Transfer.Event { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferInitiated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferInitiated.java new file mode 100644 index 0000000..47d07fe --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferInitiated.java @@ -0,0 +1,15 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; + +public record TransferInitiated( + TransferId transferId, + AccountId sourceAccountId, + AccountId destinationAccountId, + Amount amount, + Instant initiatedAt +) implements Transfer.Event { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/timing.json new file mode 100644 index 0000000..1a3e9c8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-3/transfers-records-modern/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 22155, + "duration_ms": 60695, + "total_duration_seconds": 60.7 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..125c9ac --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-4/pricing-plain-java/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..1f19147 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,25 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either applyDiscount(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + }) + .orElse(left("Price list not found: " + priceListId)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/Discount.java new file mode 100644 index 0000000..17d9648 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/Discount.java @@ -0,0 +1,51 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public static Discount of(int percentage) { + return new Discount(percentage); + } + + public Money applyTo(Money price) { + BigDecimal discountAmount = price.amount() + .multiply(BigDecimal.valueOf(percentage)) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + BigDecimal discountedAmount = price.amount().subtract(discountAmount); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + return Money.zero(price.currency()); + } + return Money.of(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..80e2bb1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceEntry.java @@ -0,0 +1,55 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice, Discount discount) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = discount; + } + + Money effectivePrice() { + if (discount == null) { + return basePrice; + } + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceList.java new file mode 100644 index 0000000..6f5029d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceList.java @@ -0,0 +1,94 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(PriceEntry entry, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(entry); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entryOpt = findEntryByProductId(productId); + if (!entryOpt.isPresent()) { + return left("No price entry found for product: " + productId); + } + PriceEntry entry = entryOpt.get(); + entry.applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private Optional findEntryByProductId(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PricingService.java new file mode 100644 index 0000000..b4aca44 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/outputs/PricingService.java @@ -0,0 +1,16 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst() + .map(PriceEntry::effectivePrice); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/timing.json new file mode 100644 index 0000000..7595809 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 34747, + "duration_ms": 105543, + "total_duration_seconds": 105.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..4dad355 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-4/pricing-plain-java/without_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..dfb544c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either applyDiscount(PriceListId priceListId, ProductId productId, Discount discount) { + return priceListRepository.findById(priceListId) + .map(priceList -> applyDiscountTo(priceList, productId, discount)) + .orElse(left("Price list not found: " + priceListId)); + } + + private Either applyDiscountTo(PriceList priceList, ProductId productId, Discount discount) { + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/Discount.java new file mode 100644 index 0000000..bbd1498 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/Discount.java @@ -0,0 +1,56 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public static Discount of(int percentage) { + return new Discount(percentage); + } + + public static Discount none() { + return new Discount(0); + } + + public Money applyTo(Money price) { + BigDecimal discountAmount = price.amount() + .multiply(BigDecimal.valueOf(percentage)) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + Money reduction = Money.of(discountAmount, price.currency()); + Money result = price.subtract(reduction); + if (!result.isPositive() && result.amount().compareTo(BigDecimal.ZERO) != 0) { + return Money.zero(price.currency()); + } + return result; + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..ecebb56 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceEntry.java @@ -0,0 +1,52 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = Discount.none(); + } + + public Money effectivePrice() { + return discount.applyTo(basePrice); + } + + public void applyDiscount(Discount discount) { + this.discount = discount; + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceList.java new file mode 100644 index 0000000..8e0c3a3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceList.java @@ -0,0 +1,93 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entry = findEntry(productId); + if (entry.isEmpty()) { + return left("Price entry not found for product: " + productId); + } + entry.get().applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PricingService.java new file mode 100644 index 0000000..14b12be --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/outputs/PricingService.java @@ -0,0 +1,27 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + private final PriceListRepository priceListRepository; + + public PricingService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Optional calculatePrice(ProductId productId) { + return priceListRepository.findActive() + .flatMap(priceList -> findEffectivePrice(priceList, productId)); + } + + private Optional findEffectivePrice(PriceList priceList, ProductId productId) { + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/timing.json new file mode 100644 index 0000000..36e054c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/pricing-plain-java/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28293, + "duration_ms": 82218, + "total_duration_seconds": 82.2 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/grading.json new file mode 100644 index 0000000..79f1711 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-4/shipping-lombok-spring/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..a2fbae1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..90c9f35 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + if (trackingNumber == null) { + throw new IllegalStateException("A shipment can only be dispatched if it has a tracking number assigned"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be delivered again"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..b8bc97a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,24 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public static ShippingAddress of(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + return new ShippingAddress(street, city, postalCode, country); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..11eab14 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public static TrackingNumber of(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number cannot be blank"); + } + return new TrackingNumber(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/timing.json new file mode 100644 index 0000000..5867828 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 38125, + "duration_ms": 113350, + "total_duration_seconds": 113.4 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/grading.json new file mode 100644 index 0000000..a0577fc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-4/shipping-lombok-spring/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..295f802 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,34 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.time.Clock; +import java.time.Instant; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + private final Clock clock; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + this.clock = clock; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + ShipmentId shipmentId = ShipmentId.generate(); + Shipment shipment = Shipment.createForOrder(shipmentId, orderId, address, Instant.now(clock)); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipmentId; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..52ba64e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,32 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +import java.time.Clock; +import java.time.Instant; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + private final Clock clock; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + this.clock = clock; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(Instant.now(clock)); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..a432acf --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,33 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Clock; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + return new CreateShipmentService(shipmentRepository, eventPublisher, clock); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher, + Clock clock) { + return new DispatchShipmentService(shipmentRepository, eventPublisher, clock); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..5dafa6c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,44 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; +import com.example.orders.shipping.domain.TrackingNumber; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void assignTrackingNumber(ShipmentId shipmentId, TrackingNumber trackingNumber) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.assignTrackingNumber(trackingNumber); + shipmentRepository.save(shipment); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..278b2ea --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,61 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(ShipmentId id, OrderId orderId, ShippingAddress address, Instant createdAt) { + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, createdAt)); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch(Instant dispatchedAt) { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Cannot dispatch a delivered shipment"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, dispatchedAt)); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..212489d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number cannot be blank"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/timing.json new file mode 100644 index 0000000..f49d585 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/shipping-lombok-spring/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28833, + "duration_ms": 77908, + "total_duration_seconds": 77.9 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/grading.json new file mode 100644 index 0000000..9b79a44 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-4/transfers-records-modern/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..40f803f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..cf7d49f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,24 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + + public InitiateTransferHandler(TransferRepository transferRepository) { + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/Transfer.java new file mode 100644 index 0000000..8ebf1e9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/Transfer.java @@ -0,0 +1,74 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.value().signum() == 0) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/timing.json new file mode 100644 index 0000000..ca9991a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 27944, + "duration_ms": 74238, + "total_duration_seconds": 74.2 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/grading.json new file mode 100644 index 0000000..c7e23f5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-4/transfers-records-modern/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated', 'TransferCompleted', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..40f803f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..d375b19 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,39 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.AccountRepository; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; +import dev.app.banking.shared.DomainException; + +public final class InitiateTransferHandler implements CommandHandler { + + private final AccountRepository accountRepository; + private final TransferRepository transferRepository; + + public InitiateTransferHandler(AccountRepository accountRepository, TransferRepository transferRepository) { + this.accountRepository = accountRepository; + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + + var sourceAccount = accountRepository.findById(sourceAccountId) + .orElseThrow(() -> new DomainException("Source account not found: " + sourceAccountId)); + + accountRepository.findById(destinationAccountId) + .orElseThrow(() -> new DomainException("Destination account not found: " + destinationAccountId)); + + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + + sourceAccount.withdraw(amount); + accountRepository.save(sourceAccount); + transferRepository.save(transfer); + + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/Transfer.java new file mode 100644 index 0000000..4457637 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/Transfer.java @@ -0,0 +1,73 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.DomainException; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new DomainException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.equals(Amount.zero())) { + throw new DomainException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new DomainException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new DomainException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferCompleted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferCompleted.java new file mode 100644 index 0000000..8a63688 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferCompleted.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import java.time.Instant; + +public record TransferCompleted( + TransferId transferId, + Instant completedAt +) implements Transfer.Event { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferInitiated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferInitiated.java new file mode 100644 index 0000000..47d07fe --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferInitiated.java @@ -0,0 +1,15 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; + +public record TransferInitiated( + TransferId transferId, + AccountId sourceAccountId, + AccountId destinationAccountId, + Amount amount, + Instant initiatedAt +) implements Transfer.Event { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/timing.json new file mode 100644 index 0000000..99f48ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-4/transfers-records-modern/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 22937, + "duration_ms": 60486, + "total_duration_seconds": 60.5 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/grading.json new file mode 100644 index 0000000..e6f9054 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/grading.json @@ -0,0 +1,62 @@ +{ + "eval_name": "order-delta", + "output_dir": "iteration-5/order-delta/with_skill/outputs", + "pass_rate": "11/11", + "expectations": [ + { + "text": "order_modified_not_recreated", + "passed": true, + "evidence": "has Order: True, old methods: True, new methods: True" + }, + { + "text": "status_enum_extended", + "passed": true, + "evidence": "CANCELLED: True, DRAFT: True, PLACED: True" + }, + { + "text": "new_fields_added", + "passed": true, + "evidence": "shippingAddress: True, discountCode: True" + }, + { + "text": "new_vo_classes_created", + "passed": true, + "evidence": "DiscountCode: True, ShippingAddress: True" + }, + { + "text": "discount_validation", + "passed": true, + "evidence": "percentage validation: True" + }, + { + "text": "shipping_address_validation", + "passed": true, + "evidence": "address validation: True" + }, + { + "text": "cancel_rule_enforced", + "passed": true, + "evidence": "cancel only-placed guard: True" + }, + { + "text": "order_cancelled_event", + "passed": true, + "evidence": "OrderCancelled event: True" + }, + { + "text": "cancel_order_service", + "passed": true, + "evidence": "CancelOrderService: True" + }, + { + "text": "existing_types_not_duplicated", + "passed": true, + "evidence": "all existing types in correct package or not emitted" + }, + { + "text": "records_style_preserved", + "passed": true, + "evidence": "DiscountCode record: True, ShippingAddress record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java new file mode 100644 index 0000000..324301a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java @@ -0,0 +1,24 @@ +package org.store.orders.application; + +import java.time.Instant; + +import org.store.orders.domain.OrderCancelled; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class CancelOrderService { + + private final OrderRepository orderRepository; + + public CancelOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderCancelled cancel(OrderId orderId, Instant now) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + var event = order.cancel(now); + orderRepository.save(order); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/PlaceOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/PlaceOrderService.java new file mode 100644 index 0000000..dc6b447 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/application/PlaceOrderService.java @@ -0,0 +1,21 @@ +package org.store.orders.application; + +import org.store.orders.domain.Order; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class PlaceOrderService { + + private final OrderRepository orderRepository; + + public PlaceOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public void place(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + order.place(); + orderRepository.save(order); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/CustomerId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/CustomerId.java new file mode 100644 index 0000000..d5170b3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/CustomerId.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record CustomerId(UUID value) { + public CustomerId { + Objects.requireNonNull(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java new file mode 100644 index 0000000..322c6c1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java @@ -0,0 +1,16 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; + +public record DiscountCode(String code, int percentage) { + + public DiscountCode { + if (code == null || code.isBlank()) throw new IllegalArgumentException("Code is required"); + if (percentage < 0 || percentage > 100) throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + + public Money applyTo(Money money) { + var multiplier = BigDecimal.ONE.subtract(BigDecimal.valueOf(percentage).divide(BigDecimal.valueOf(100))); + return Money.of(money.amount().multiply(multiplier), money.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Money.java new file mode 100644 index 0000000..cc8beab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Money.java @@ -0,0 +1,23 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; + +public record Money(BigDecimal amount, String currency) { + public Money { + if (amount == null) throw new IllegalArgumentException("Amount cannot be null"); + if (currency == null || currency.isBlank()) throw new IllegalArgumentException("Currency required"); + } + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + public Money add(Money other) { + if (!currency.equals(other.currency)) throw new IllegalArgumentException("Currency mismatch"); + return new Money(amount.add(other.amount), currency); + } + public boolean isPositive() { + return amount.compareTo(BigDecimal.ZERO) > 0; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Order.java new file mode 100644 index 0000000..e3f11d6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/Order.java @@ -0,0 +1,87 @@ +package org.store.orders.domain; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class Order { + + enum Status { DRAFT, PLACED, CANCELLED } + + private final OrderId id; + private final CustomerId customerId; + private final List lines; + private Status status; + private final Instant createdAt; + private ShippingAddress shippingAddress; + private DiscountCode discountCode; + + private Order(OrderId id, CustomerId customerId, Instant createdAt) { + this.id = id; + this.customerId = customerId; + this.lines = new ArrayList<>(); + this.status = Status.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(CustomerId customerId, Instant now) { + return new Order(OrderId.generate(), customerId, now); + } + + public void addLine(String productName, int quantity, Money unitPrice) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productName, quantity, unitPrice)); + } + + public OrderCancelled cancel(Instant now) { + if (status != Status.PLACED) { + throw new IllegalStateException("Only placed orders can be cancelled"); + } + this.status = Status.CANCELLED; + return new OrderCancelled(id, now); + } + + public void applyDiscount(DiscountCode discountCode) { + this.discountCode = discountCode; + } + + public Money discountedTotal() { + var orderTotal = total(); + if (discountCode == null) { + return orderTotal; + } + return discountCode.applyTo(orderTotal); + } + + public void place() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot place an empty order"); + } + this.status = Status.PLACED; + } + + public void setShippingAddress(ShippingAddress shippingAddress) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Shipping address can only be set on draft orders"); + } + this.shippingAddress = shippingAddress; + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public List lines() { return Collections.unmodifiableList(lines); } + public ShippingAddress shippingAddress() { return shippingAddress; } + public DiscountCode discountCode() { return discountCode; } + public boolean isDraft() { return status == Status.DRAFT; } + public boolean isPlaced() { return status == Status.PLACED; } + public boolean isCancelled() { return status == Status.CANCELLED; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java new file mode 100644 index 0000000..764925f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java @@ -0,0 +1,11 @@ +package org.store.orders.domain; + +import java.time.Instant; + +public record OrderCancelled(OrderId orderId, Instant cancelledAt) { + + public OrderCancelled { + if (orderId == null) throw new IllegalArgumentException("Order id is required"); + if (cancelledAt == null) throw new IllegalArgumentException("Cancelled at is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderId.java new file mode 100644 index 0000000..95b4383 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderId.java @@ -0,0 +1,13 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record OrderId(UUID value) { + public OrderId { + Objects.requireNonNull(value); + } + public static OrderId generate() { + return new OrderId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderLine.java new file mode 100644 index 0000000..d2bd921 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderLine.java @@ -0,0 +1,12 @@ +package org.store.orders.domain; + +record OrderLine(String productName, int quantity, Money unitPrice) { + + OrderLine { + if (quantity <= 0) throw new IllegalArgumentException("Quantity must be positive"); + } + + Money lineTotal() { + return Money.of(unitPrice.amount().multiply(java.math.BigDecimal.valueOf(quantity)), unitPrice.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderRepository.java new file mode 100644 index 0000000..1f11aa9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/OrderRepository.java @@ -0,0 +1,8 @@ +package org.store.orders.domain; + +import java.util.Optional; + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java new file mode 100644 index 0000000..604e7a2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +public record ShippingAddress(String street, String city, String postalCode) { + + public ShippingAddress { + if (street == null || street.isBlank()) throw new IllegalArgumentException("Street is required"); + if (city == null || city.isBlank()) throw new IllegalArgumentException("City is required"); + if (postalCode == null || postalCode.isBlank()) throw new IllegalArgumentException("Postal code is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/timing.json new file mode 100644 index 0000000..1fe4b7b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 28030, + "duration_ms": 82313, + "total_duration_seconds": 82.3 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/grading.json new file mode 100644 index 0000000..11a520e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/grading.json @@ -0,0 +1,62 @@ +{ + "eval_name": "order-delta", + "output_dir": "iteration-5/order-delta/without_skill/outputs", + "pass_rate": "11/11", + "expectations": [ + { + "text": "order_modified_not_recreated", + "passed": true, + "evidence": "has Order: True, old methods: True, new methods: True" + }, + { + "text": "status_enum_extended", + "passed": true, + "evidence": "CANCELLED: True, DRAFT: True, PLACED: True" + }, + { + "text": "new_fields_added", + "passed": true, + "evidence": "shippingAddress: True, discountCode: True" + }, + { + "text": "new_vo_classes_created", + "passed": true, + "evidence": "DiscountCode: True, ShippingAddress: True" + }, + { + "text": "discount_validation", + "passed": true, + "evidence": "percentage validation: True" + }, + { + "text": "shipping_address_validation", + "passed": true, + "evidence": "address validation: True" + }, + { + "text": "cancel_rule_enforced", + "passed": true, + "evidence": "cancel only-placed guard: True" + }, + { + "text": "order_cancelled_event", + "passed": true, + "evidence": "OrderCancelled event: True" + }, + { + "text": "cancel_order_service", + "passed": true, + "evidence": "CancelOrderService: True" + }, + { + "text": "existing_types_not_duplicated", + "passed": true, + "evidence": "all existing types in correct package or not emitted" + }, + { + "text": "records_style_preserved", + "passed": true, + "evidence": "DiscountCode record: True, ShippingAddress record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java new file mode 100644 index 0000000..668bcbe --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java @@ -0,0 +1,24 @@ +package org.store.orders.application; + +import java.time.Instant; + +import org.store.orders.domain.OrderCancelled; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class CancelOrderService { + + private final OrderRepository orderRepository; + + public CancelOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderCancelled cancel(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + var event = order.cancel(Instant.now()); + orderRepository.save(order); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java new file mode 100644 index 0000000..5a174d6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java @@ -0,0 +1,18 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public record DiscountCode(String code, int percentage) { + public DiscountCode { + if (code == null || code.isBlank()) throw new IllegalArgumentException("Code is required"); + if (percentage < 0 || percentage > 100) throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + + public Money applyTo(Money money) { + BigDecimal factor = BigDecimal.ONE.subtract( + BigDecimal.valueOf(percentage).divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) + ); + return Money.of(money.amount().multiply(factor).setScale(2, RoundingMode.HALF_UP), money.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java new file mode 100644 index 0000000..d09bf69 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java @@ -0,0 +1,87 @@ +package org.store.orders.domain; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class Order { + + enum Status { DRAFT, PLACED, CANCELLED } + + private final OrderId id; + private final CustomerId customerId; + private final List lines; + private Status status; + private final Instant createdAt; + private ShippingAddress shippingAddress; + private DiscountCode discountCode; + + private Order(OrderId id, CustomerId customerId, Instant createdAt) { + this.id = id; + this.customerId = customerId; + this.lines = new ArrayList<>(); + this.status = Status.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(CustomerId customerId, Instant now) { + return new Order(OrderId.generate(), customerId, now); + } + + public void addLine(String productName, int quantity, Money unitPrice) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productName, quantity, unitPrice)); + } + + public void applyDiscount(DiscountCode discountCode) { + this.discountCode = discountCode; + } + + public OrderCancelled cancel(Instant now) { + if (status != Status.PLACED) { + throw new IllegalStateException("Only placed orders can be cancelled"); + } + this.status = Status.CANCELLED; + return new OrderCancelled(id, now); + } + + public Money discountedTotal() { + Money orderTotal = total(); + if (discountCode == null) { + return orderTotal; + } + return discountCode.applyTo(orderTotal); + } + + public void place() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot place an empty order"); + } + this.status = Status.PLACED; + } + + public void setShippingAddress(ShippingAddress shippingAddress) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Shipping address can only be set on draft orders"); + } + this.shippingAddress = shippingAddress; + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public List lines() { return Collections.unmodifiableList(lines); } + public boolean isDraft() { return status == Status.DRAFT; } + public boolean isPlaced() { return status == Status.PLACED; } + public boolean isCancelled() { return status == Status.CANCELLED; } + public ShippingAddress shippingAddress() { return shippingAddress; } + public DiscountCode discountCode() { return discountCode; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java new file mode 100644 index 0000000..9a1af15 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +import java.time.Instant; + +public record OrderCancelled(OrderId orderId, Instant cancelledAt) { + public OrderCancelled { + if (orderId == null) throw new IllegalArgumentException("Order ID is required"); + if (cancelledAt == null) throw new IllegalArgumentException("Cancelled at is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java new file mode 100644 index 0000000..b3bf2e3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java @@ -0,0 +1,9 @@ +package org.store.orders.domain; + +public record ShippingAddress(String street, String city, String postalCode) { + public ShippingAddress { + if (street == null || street.isBlank()) throw new IllegalArgumentException("Street is required"); + if (city == null || city.isBlank()) throw new IllegalArgumentException("City is required"); + if (postalCode == null || postalCode.isBlank()) throw new IllegalArgumentException("Postal code is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/timing.json new file mode 100644 index 0000000..d15f09a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/order-delta/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 21115, + "duration_ms": 58838, + "total_duration_seconds": 58.8 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/grading.json new file mode 100644 index 0000000..f22f799 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-5/payroll-mixed/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": true, + "evidence": "imports EmployeeId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": true, + "evidence": "Payslip package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java new file mode 100644 index 0000000..988e602 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java @@ -0,0 +1,27 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(payrollRun -> { + Either result = payrollRun.execute(); + result.peek(event -> payrollRunRepository.save(payrollRun)); + return result; + }) + .orElse(left("Payroll run not found")); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java new file mode 100644 index 0000000..3adb3ed --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java @@ -0,0 +1,50 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { + return payrollRunId; + } + + public YearMonth month() { + return month; + } + + public BigDecimal totalNetPay() { + return totalNetPay; + } + + public Instant executedAt() { + return executedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java new file mode 100644 index 0000000..21bf80d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java @@ -0,0 +1,86 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(PayrollRunId id, YearMonth month) { + return new PayrollRun(id, month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status == PayrollStatus.EXECUTED) { + return left("An executed payroll run cannot be modified"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status == PayrollStatus.EXECUTED) { + return left("A payroll run can only be executed once"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + payslips.forEach(Payslip::markPaid); + this.status = PayrollStatus.EXECUTED; + BigDecimal totalNetPay = payslips.stream() + .map(p -> p.salary().netPay()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { + return id; + } + + public YearMonth month() { + return month; + } + + public List payslips() { + return Collections.unmodifiableList(payslips); + } + + public PayrollStatus status() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java new file mode 100644 index 0000000..2cbf9ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java @@ -0,0 +1,8 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + void save(PayrollRun payrollRun); + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java new file mode 100644 index 0000000..c19c5ee --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java @@ -0,0 +1,47 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.Objects; + +class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + public EmployeeId employeeId() { + return employeeId; + } + + public Salary salary() { + return salary; + } + + public boolean isPaid() { + return paid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java new file mode 100644 index 0000000..bde64e7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java @@ -0,0 +1,47 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { + return grossAmount; + } + + public BigDecimal deductions() { + return deductions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/timing.json new file mode 100644 index 0000000..c67c702 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 32399, + "duration_ms": 94208, + "total_duration_seconds": 94.2 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/grading.json new file mode 100644 index 0000000..57da5d7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-5/payroll-mixed/without_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": true, + "evidence": "imports EmployeeId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": false, + "evidence": "Payslip package-private: False" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java new file mode 100644 index 0000000..8cd8d0a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java @@ -0,0 +1,24 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRun; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(PayrollRun::execute) + .orElse(left("Payroll run not found")); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java new file mode 100644 index 0000000..471f446 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { return payrollRunId; } + public YearMonth month() { return month; } + public BigDecimal totalNetPay() { return totalNetPay; } + public Instant executedAt() { return executedAt; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java new file mode 100644 index 0000000..d0f1463 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java @@ -0,0 +1,75 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(YearMonth month) { + return new PayrollRun(PayrollRunId.generate(), month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status != PayrollStatus.DRAFT) { + return left("Cannot modify an executed payroll run"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status != PayrollStatus.DRAFT) { + return left("Payroll run has already been executed"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + this.status = PayrollStatus.EXECUTED; + payslips.forEach(Payslip::markPaid); + BigDecimal totalNetPay = payslips.stream() + .map(p -> p.salary().netPay()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { return id; } + public YearMonth month() { return month; } + public List payslips() { return Collections.unmodifiableList(payslips); } + public PayrollStatus status() { return status; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java new file mode 100644 index 0000000..2cbf9ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java @@ -0,0 +1,8 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + void save(PayrollRun payrollRun); + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Payslip.java new file mode 100644 index 0000000..662d509 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Payslip.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.Objects; + +public class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + public EmployeeId employeeId() { return employeeId; } + public Salary salary() { return salary; } + public boolean isPaid() { return paid; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Salary.java new file mode 100644 index 0000000..c132f8b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/outputs/com/acme/hr/payroll/domain/Salary.java @@ -0,0 +1,45 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + if (deductions.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Deductions cannot be negative"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { return grossAmount; } + public BigDecimal deductions() { return deductions; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/timing.json new file mode 100644 index 0000000..1d6a250 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-5/payroll-mixed/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 25828, + "duration_ms": 78316, + "total_duration_seconds": 78.3 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/grading.json new file mode 100644 index 0000000..6564b01 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-6/receiving-unusual/with_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "factory_as_inner_class", + "passed": false, + "evidence": "nested Factory in ReceivingNote: False" + }, + { + "text": "sku_reused", + "passed": true, + "evidence": "imports Sku: True, creates new: False" + }, + { + "text": "quantity_reused", + "passed": true, + "evidence": "imports Quantity: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java new file mode 100644 index 0000000..25b0a9b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize_(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivedLine.java new file mode 100644 index 0000000..4b68eb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivedLine.java @@ -0,0 +1,25 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + Quantity discrepancy() { + return new Quantity(expectedQuantity.value() - actualQuantity.value()); + } + + Sku sku() { return sku; } + Quantity expectedQuantity() { return expectedQuantity; } + Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNote.java new file mode 100644 index 0000000..b1e2373 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNote.java @@ -0,0 +1,67 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.OPEN; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity quantity) { + if (status == ReceivingStatus.FINALIZED) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (quantity.value() < 0) { + throw new IllegalArgumentException("Received quantity must be non-negative"); + } + lines.add(new ReceivedLine(sku, quantity, quantity)); + } + + public ReceivingEvents.ReceivingFinalized finalize_() { + if (status == ReceivingStatus.FINALIZED) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.FINALIZED; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return Collections.unmodifiableList(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingStatus.java new file mode 100644 index 0000000..ddc4d7a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/outputs/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + OPEN, + FINALIZED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/timing.json new file mode 100644 index 0000000..3cda1b5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/with_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 25377, + "duration_ms": 74138, + "total_duration_seconds": 74.1 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/grading.json new file mode 100644 index 0000000..aa8fb68 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-6/receiving-unusual/without_skill/outputs", + "pass_rate": "6/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "factory_as_inner_class", + "passed": false, + "evidence": "nested Factory in ReceivingNote: False" + }, + { + "text": "sku_reused", + "passed": false, + "evidence": "imports Sku: False, creates new: True" + }, + { + "text": "quantity_reused", + "passed": false, + "evidence": "imports Quantity: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/FinalizeReceivingHandler.java new file mode 100644 index 0000000..9b5e424 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Quantity.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Quantity.java new file mode 100644 index 0000000..55c0174 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Quantity.java @@ -0,0 +1,11 @@ +package io.proj.warehouse.receiving; + +public record Quantity(int value) { + public Quantity { + if (value < 0) throw new IllegalArgumentException("Quantity cannot be negative"); + } + + public static Quantity zero() { return new Quantity(0); } + + public Quantity subtract(Quantity other) { return new Quantity(value - other.value); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivedLine.java new file mode 100644 index 0000000..4577ae7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivedLine.java @@ -0,0 +1,22 @@ +package io.proj.warehouse.receiving; + +public final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + public ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + public Quantity discrepancy() { + return expectedQuantity.subtract(actualQuantity); + } + + public Sku sku() { return sku; } + public Quantity expectedQuantity() { return expectedQuantity; } + public Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNote.java new file mode 100644 index 0000000..e4a38e6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNote.java @@ -0,0 +1,62 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.OPEN; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity expectedQuantity) { + ensureNotFinalized(); + lines.add(new ReceivedLine(sku, expectedQuantity, Quantity.zero())); + } + + public ReceivingEvents.ReceivingFinalized finalize() { + ensureNotFinalized(); + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.FINALIZED; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return List.copyOf(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + private void ensureNotFinalized() { + if (status == ReceivingStatus.FINALIZED) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingStatus.java new file mode 100644 index 0000000..ddc4d7a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + OPEN, + FINALIZED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Sku.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Sku.java new file mode 100644 index 0000000..4e5f19e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/outputs/io/proj/warehouse/receiving/Sku.java @@ -0,0 +1,10 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; + +public record Sku(String value) { + public Sku { + Objects.requireNonNull(value); + if (value.isBlank()) throw new IllegalArgumentException("SKU cannot be blank"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/timing.json new file mode 100644 index 0000000..eacbfdc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/receiving-unusual/without_skill/timing.json @@ -0,0 +1,5 @@ +{ + "total_tokens": 19768, + "duration_ms": 58061, + "total_duration_seconds": 58.1 +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/grading.json new file mode 100644 index 0000000..bdfec00 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "returns", + "output_dir": "iteration-6/returns-cross-module/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "return_request_in_sales_package", + "passed": true, + "evidence": "sales.returns package: True" + }, + { + "text": "return_receipt_in_fulfillment_package", + "passed": true, + "evidence": "fulfillment package: True" + }, + { + "text": "sealed_events_pattern", + "passed": true, + "evidence": "ReturnRequest events: True, ReturnReceipt events: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "ReturnRequest.Repository: True, ReturnReceipt.Repository: True" + }, + { + "text": "orderid_reused_from_shared", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + }, + { + "text": "money_reused_from_shared", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReturnRequest', 'ReturnRequestId', 'ReturnRequested', 'ReturnReceipt', 'ReturnReceiptId', 'ReturnReceived']" + }, + { + "text": "cross_module_reference", + "passed": true, + "evidence": "ReturnReceipt references ReturnRequestId: True" + }, + { + "text": "return_status_present", + "passed": true, + "evidence": "ReturnStatus: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java new file mode 100644 index 0000000..9dc0553 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java @@ -0,0 +1,53 @@ +package net.ecom.fulfillment.returnprocessing; + +import net.ecom.sales.shared.ReturnRequestId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReturnReceipt { + + public sealed interface Event permits ReturnReceived {} + public record ReturnReceived(ReturnReceiptId returnReceiptId, ReturnRequestId returnRequestId, Instant receivedAt) implements Event {} + + private final ReturnReceiptId id; + private final ReturnRequestId returnRequestId; + private Instant receivedAt; + private boolean inspected = false; + private final List pendingEvents = new ArrayList<>(); + + private ReturnReceipt(ReturnReceiptId id, ReturnRequestId returnRequestId, Instant receivedAt) { + this.id = id; + this.returnRequestId = returnRequestId; + this.receivedAt = receivedAt; + } + + public static ReturnReceipt receive(ReturnRequestId returnRequestId) { + var id = ReturnReceiptId.generate(); + var receivedAt = Instant.now(); + var receipt = new ReturnReceipt(id, returnRequestId, receivedAt); + receipt.pendingEvents.add(new ReturnReceived(id, returnRequestId, receivedAt)); + return receipt; + } + + public void inspect() { + this.inspected = true; + } + + public ReturnReceiptId id() { return id; } + public ReturnRequestId returnRequestId() { return returnRequestId; } + public Instant receivedAt() { return receivedAt; } + public boolean inspected() { return inspected; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnReceipt returnReceipt); + ReturnReceipt findById(ReturnReceiptId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java new file mode 100644 index 0000000..693765a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java @@ -0,0 +1,9 @@ +package net.ecom.fulfillment.returnprocessing; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnReceiptId(UUID value) { + public ReturnReceiptId { Objects.requireNonNull(value); } + public static ReturnReceiptId generate() { return new ReturnReceiptId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java new file mode 100644 index 0000000..f95d520 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java @@ -0,0 +1,58 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; +import net.ecom.sales.shared.ReturnRequestId; + +import java.util.ArrayList; +import java.util.List; + +public final class ReturnRequest { + + public sealed interface Event permits ReturnRequested {} + public record ReturnRequested(ReturnRequestId returnRequestId, OrderId orderId, Money refundAmount) implements Event {} + + enum ReturnStatus { PENDING, APPROVED } + + private final ReturnRequestId id; + private final OrderId orderId; + private final String reason; + private final Money refundAmount; + private ReturnStatus status = ReturnStatus.PENDING; + private final List pendingEvents = new ArrayList<>(); + + private ReturnRequest(ReturnRequestId id, OrderId orderId, String reason, Money refundAmount) { + this.id = id; + this.orderId = orderId; + this.reason = reason; + this.refundAmount = refundAmount; + } + + public static ReturnRequest submit(OrderId orderId, Money refundAmount, String reason) { + var request = new ReturnRequest(ReturnRequestId.generate(), orderId, reason, refundAmount); + request.pendingEvents.add(new ReturnRequested(request.id, orderId, refundAmount)); + return request; + } + + public void approve() { + if (status != ReturnStatus.PENDING) throw new IllegalStateException("An approved return cannot be modified"); + this.status = ReturnStatus.APPROVED; + } + + public ReturnRequestId id() { return id; } + public OrderId orderId() { return orderId; } + public String reason() { return reason; } + public Money refundAmount() { return refundAmount; } + public ReturnStatus status() { return status; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnRequest returnRequest); + ReturnRequest findById(ReturnRequestId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java new file mode 100644 index 0000000..1d6efb9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java @@ -0,0 +1,18 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +public final class SubmitReturnHandler { + + private final ReturnRequest.Repository returnRequestRepository; + + public SubmitReturnHandler(ReturnRequest.Repository returnRequestRepository) { + this.returnRequestRepository = returnRequestRepository; + } + + public void handle(OrderId orderId, Money refundAmount, String reason) { + var returnRequest = ReturnRequest.submit(orderId, refundAmount, reason); + returnRequestRepository.save(returnRequest); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java new file mode 100644 index 0000000..ed0585d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java @@ -0,0 +1,9 @@ +package net.ecom.sales.shared; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnRequestId(UUID value) { + public ReturnRequestId { Objects.requireNonNull(value); } + public static ReturnRequestId generate() { return new ReturnRequestId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/timing.json new file mode 100644 index 0000000..87adb8e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/with_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 25993, "duration_ms": 73640, "total_duration_seconds": 73.6} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/grading.json new file mode 100644 index 0000000..025fdab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "returns", + "output_dir": "iteration-6/returns-cross-module/without_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "return_request_in_sales_package", + "passed": true, + "evidence": "sales.returns package: True" + }, + { + "text": "return_receipt_in_fulfillment_package", + "passed": true, + "evidence": "fulfillment package: True" + }, + { + "text": "sealed_events_pattern", + "passed": true, + "evidence": "ReturnRequest events: True, ReturnReceipt events: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "ReturnRequest.Repository: True, ReturnReceipt.Repository: True" + }, + { + "text": "orderid_reused_from_shared", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + }, + { + "text": "money_reused_from_shared", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReturnRequest', 'ReturnRequestId', 'ReturnRequested', 'ReturnReceipt', 'ReturnReceiptId', 'ReturnReceived']" + }, + { + "text": "cross_module_reference", + "passed": true, + "evidence": "ReturnReceipt references ReturnRequestId: True" + }, + { + "text": "return_status_present", + "passed": false, + "evidence": "ReturnStatus: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java new file mode 100644 index 0000000..b18858b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java @@ -0,0 +1,54 @@ +package net.ecom.fulfillment.returnprocessing; + +import net.ecom.sales.shared.ReturnRequestId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReturnReceipt { + + public sealed interface Event permits ReturnReceived {} + public record ReturnReceived(ReturnReceiptId returnReceiptId, ReturnRequestId returnRequestId, Instant receivedAt) implements Event {} + + private final ReturnReceiptId id; + private final ReturnRequestId returnRequestId; + private Instant receivedAt; + private boolean inspected = false; + private final List pendingEvents = new ArrayList<>(); + + private ReturnReceipt(ReturnReceiptId id, ReturnRequestId returnRequestId, Instant receivedAt) { + this.id = id; + this.returnRequestId = returnRequestId; + this.receivedAt = receivedAt; + } + + public static ReturnReceipt receive(ReturnRequestId returnRequestId) { + var id = ReturnReceiptId.generate(); + var now = Instant.now(); + var receipt = new ReturnReceipt(id, returnRequestId, now); + receipt.pendingEvents.add(new ReturnReceived(id, returnRequestId, now)); + return receipt; + } + + public void inspect() { + if (inspected) throw new IllegalStateException("Already inspected"); + this.inspected = true; + } + + public ReturnReceiptId id() { return id; } + public ReturnRequestId returnRequestId() { return returnRequestId; } + public Instant receivedAt() { return receivedAt; } + public boolean isInspected() { return inspected; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnReceipt receipt); + ReturnReceipt findByReturnRequestId(ReturnRequestId returnRequestId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java new file mode 100644 index 0000000..693765a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java @@ -0,0 +1,9 @@ +package net.ecom.fulfillment.returnprocessing; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnReceiptId(UUID value) { + public ReturnReceiptId { Objects.requireNonNull(value); } + public static ReturnReceiptId generate() { return new ReturnReceiptId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java new file mode 100644 index 0000000..0156e26 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java @@ -0,0 +1,57 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; +import net.ecom.sales.shared.ReturnRequestId; + +import java.util.ArrayList; +import java.util.List; + +public final class ReturnRequest { + + public sealed interface Event permits ReturnRequested {} + public record ReturnRequested(ReturnRequestId returnRequestId, OrderId orderId, Money refundAmount) implements Event {} + + enum Status { PENDING, APPROVED } + + private final ReturnRequestId id; + private final OrderId orderId; + private final String reason; + private final Money refundAmount; + private Status status = Status.PENDING; + private final List pendingEvents = new ArrayList<>(); + + private ReturnRequest(ReturnRequestId id, OrderId orderId, String reason, Money refundAmount) { + this.id = id; + this.orderId = orderId; + this.reason = reason; + this.refundAmount = refundAmount; + } + + public static ReturnRequest submit(OrderId orderId, Money orderTotal) { + var request = new ReturnRequest(ReturnRequestId.generate(), orderId, "", orderTotal); + request.pendingEvents.add(new ReturnRequested(request.id, orderId, orderTotal)); + return request; + } + + public void approve() { + if (status != Status.PENDING) throw new IllegalStateException("An approved return cannot be modified"); + this.status = Status.APPROVED; + } + + public ReturnRequestId id() { return id; } + public OrderId orderId() { return orderId; } + public Money refundAmount() { return refundAmount; } + public boolean isApproved() { return status == Status.APPROVED; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnRequest returnRequest); + ReturnRequest findById(ReturnRequestId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java new file mode 100644 index 0000000..3a4e91f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java @@ -0,0 +1,25 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.orders.Order; +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +public final class SubmitReturnHandler { + + private final Order.Repository orderRepository; + private final ReturnRequest.Repository returnRequestRepository; + + public SubmitReturnHandler(Order.Repository orderRepository, ReturnRequest.Repository returnRequestRepository) { + this.orderRepository = orderRepository; + this.returnRequestRepository = returnRequestRepository; + } + + public void handle(OrderId orderId, Money orderTotal) { + var order = orderRepository.findById(orderId); + if (order == null) throw new IllegalArgumentException("Order not found"); + if (!order.isPaid()) throw new IllegalStateException("Only paid orders can be returned"); + + var returnRequest = ReturnRequest.submit(orderId, orderTotal); + returnRequestRepository.save(returnRequest); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java new file mode 100644 index 0000000..ed0585d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java @@ -0,0 +1,9 @@ +package net.ecom.sales.shared; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnRequestId(UUID value) { + public ReturnRequestId { Objects.requireNonNull(value); } + public static ReturnRequestId generate() { return new ReturnRequestId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/timing.json new file mode 100644 index 0000000..3d9af54 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-6/returns-cross-module/without_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 21010, "duration_ms": 72442, "total_duration_seconds": 72.4} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/grading.json new file mode 100644 index 0000000..5310f34 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/grading.json @@ -0,0 +1,62 @@ +{ + "eval_name": "order-delta", + "output_dir": "iteration-7/order-delta/with_skill/outputs", + "pass_rate": "11/11", + "expectations": [ + { + "text": "order_modified_not_recreated", + "passed": true, + "evidence": "has Order: True, old methods: True, new methods: True" + }, + { + "text": "status_enum_extended", + "passed": true, + "evidence": "CANCELLED: True, DRAFT: True, PLACED: True" + }, + { + "text": "new_fields_added", + "passed": true, + "evidence": "shippingAddress: True, discountCode: True" + }, + { + "text": "new_vo_classes_created", + "passed": true, + "evidence": "DiscountCode: True, ShippingAddress: True" + }, + { + "text": "discount_validation", + "passed": true, + "evidence": "percentage validation: True" + }, + { + "text": "shipping_address_validation", + "passed": true, + "evidence": "address validation: True" + }, + { + "text": "cancel_rule_enforced", + "passed": true, + "evidence": "cancel only-placed guard: True" + }, + { + "text": "order_cancelled_event", + "passed": true, + "evidence": "OrderCancelled event: True" + }, + { + "text": "cancel_order_service", + "passed": true, + "evidence": "CancelOrderService: True" + }, + { + "text": "existing_types_not_duplicated", + "passed": true, + "evidence": "all existing types in correct package or not emitted" + }, + { + "text": "records_style_preserved", + "passed": true, + "evidence": "DiscountCode record: True, ShippingAddress record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java new file mode 100644 index 0000000..07d185a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/application/CancelOrderService.java @@ -0,0 +1,24 @@ +package org.store.orders.application; + +import org.store.orders.domain.OrderCancelled; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +import java.time.Instant; + +public class CancelOrderService { + + private final OrderRepository orderRepository; + + public CancelOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderCancelled cancel(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + var event = order.cancel(Instant.now()); + orderRepository.save(order); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java new file mode 100644 index 0000000..c830cc7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/DiscountCode.java @@ -0,0 +1,15 @@ +package org.store.orders.domain; + +public record DiscountCode(String code, int percentage) { + public DiscountCode { + if (code == null || code.isBlank()) throw new IllegalArgumentException("Code required"); + if (percentage < 0 || percentage > 100) throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + + public Money applyTo(Money money) { + var discountedAmount = money.amount().multiply( + java.math.BigDecimal.valueOf(100 - percentage)).divide( + java.math.BigDecimal.valueOf(100), money.amount().scale(), java.math.RoundingMode.HALF_UP); + return Money.of(discountedAmount, money.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/Order.java new file mode 100644 index 0000000..936404c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/Order.java @@ -0,0 +1,95 @@ +package org.store.orders.domain; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class Order { + + enum Status { DRAFT, PLACED, CANCELLED } + + private final OrderId id; + private final CustomerId customerId; + private final List lines; + private Status status; + private final Instant createdAt; + private ShippingAddress shippingAddress; + private DiscountCode discountCode; + private final List pendingEvents = new ArrayList<>(); + + private Order(OrderId id, CustomerId customerId, Instant createdAt) { + this.id = id; + this.customerId = customerId; + this.lines = new ArrayList<>(); + this.status = Status.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(CustomerId customerId, Instant now) { + return new Order(OrderId.generate(), customerId, now); + } + + public void addLine(String productName, int quantity, Money unitPrice) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productName, quantity, unitPrice)); + } + + public void applyDiscount(DiscountCode discountCode) { + this.discountCode = discountCode; + } + + public OrderCancelled cancel(Instant now) { + if (status != Status.PLACED) { + throw new IllegalStateException("Only placed orders can be cancelled"); + } + this.status = Status.CANCELLED; + var event = new OrderCancelled(id, now); + pendingEvents.add(event); + return event; + } + + public Money discountedTotal() { + var orderTotal = total(); + if (discountCode == null) { + return orderTotal; + } + return discountCode.applyTo(orderTotal); + } + + public void place() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot place an empty order"); + } + this.status = Status.PLACED; + } + + public void setShippingAddress(ShippingAddress shippingAddress) { + if (status != Status.DRAFT) { + throw new IllegalStateException("Shipping address can only be set on draft orders"); + } + this.shippingAddress = shippingAddress; + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public List lines() { return Collections.unmodifiableList(lines); } + public ShippingAddress shippingAddress() { return shippingAddress; } + public DiscountCode discountCode() { return discountCode; } + public boolean isDraft() { return status == Status.DRAFT; } + public boolean isPlaced() { return status == Status.PLACED; } + public boolean isCancelled() { return status == Status.CANCELLED; } + public List pendingEvents() { return Collections.unmodifiableList(pendingEvents); } + + public void flushEvents() { + pendingEvents.clear(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java new file mode 100644 index 0000000..93211dc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/OrderCancelled.java @@ -0,0 +1,6 @@ +package org.store.orders.domain; + +import java.time.Instant; + +public record OrderCancelled(OrderId orderId, Instant cancelledAt) { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java new file mode 100644 index 0000000..810231e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/outputs/org/store/orders/domain/ShippingAddress.java @@ -0,0 +1,9 @@ +package org.store.orders.domain; + +public record ShippingAddress(String street, String city, String postalCode) { + public ShippingAddress { + if (street == null || street.isBlank()) throw new IllegalArgumentException("Street required"); + if (city == null || city.isBlank()) throw new IllegalArgumentException("City required"); + if (postalCode == null || postalCode.isBlank()) throw new IllegalArgumentException("Postal code required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/timing.json new file mode 100644 index 0000000..8f9341e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/with_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 28452, "duration_ms": 89717, "total_duration_seconds": 89.7} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/grading.json new file mode 100644 index 0000000..6ec967b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/grading.json @@ -0,0 +1,62 @@ +{ + "eval_name": "order-delta", + "output_dir": "iteration-7/order-delta/without_skill/outputs", + "pass_rate": "11/11", + "expectations": [ + { + "text": "order_modified_not_recreated", + "passed": true, + "evidence": "has Order: True, old methods: True, new methods: True" + }, + { + "text": "status_enum_extended", + "passed": true, + "evidence": "CANCELLED: True, DRAFT: True, PLACED: True" + }, + { + "text": "new_fields_added", + "passed": true, + "evidence": "shippingAddress: True, discountCode: True" + }, + { + "text": "new_vo_classes_created", + "passed": true, + "evidence": "DiscountCode: True, ShippingAddress: True" + }, + { + "text": "discount_validation", + "passed": true, + "evidence": "percentage validation: True" + }, + { + "text": "shipping_address_validation", + "passed": true, + "evidence": "address validation: True" + }, + { + "text": "cancel_rule_enforced", + "passed": true, + "evidence": "cancel only-placed guard: True" + }, + { + "text": "order_cancelled_event", + "passed": true, + "evidence": "OrderCancelled event: True" + }, + { + "text": "cancel_order_service", + "passed": true, + "evidence": "CancelOrderService: True" + }, + { + "text": "existing_types_not_duplicated", + "passed": true, + "evidence": "all existing types in correct package or not emitted" + }, + { + "text": "records_style_preserved", + "passed": true, + "evidence": "DiscountCode record: True, ShippingAddress record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/pom.xml b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/pom.xml new file mode 100644 index 0000000..a7feaa5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + org.store + order-service + 1.0-SNAPSHOT + + 17 + 17 + + diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java new file mode 100644 index 0000000..e1d9e23 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/CancelOrderService.java @@ -0,0 +1,24 @@ +package org.store.orders.application; + +import java.time.Instant; + +import org.store.orders.domain.OrderCancelled; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class CancelOrderService { + + private final OrderRepository orderRepository; + + public CancelOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderCancelled cancel(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + OrderCancelled event = order.cancel(Instant.now()); + orderRepository.save(order); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/PlaceOrderService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/PlaceOrderService.java new file mode 100644 index 0000000..dc6b447 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/application/PlaceOrderService.java @@ -0,0 +1,21 @@ +package org.store.orders.application; + +import org.store.orders.domain.Order; +import org.store.orders.domain.OrderId; +import org.store.orders.domain.OrderRepository; + +public class PlaceOrderService { + + private final OrderRepository orderRepository; + + public PlaceOrderService(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public void place(OrderId orderId) { + var order = orderRepository.findById(orderId) + .orElseThrow(() -> new IllegalArgumentException("Order not found")); + order.place(); + orderRepository.save(order); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/CustomerId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/CustomerId.java new file mode 100644 index 0000000..d5170b3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/CustomerId.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record CustomerId(UUID value) { + public CustomerId { + Objects.requireNonNull(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java new file mode 100644 index 0000000..32ca070 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/DiscountCode.java @@ -0,0 +1,16 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; + +public record DiscountCode(String code, int percentage) { + + public DiscountCode { + if (code == null || code.isBlank()) throw new IllegalArgumentException("Code is required"); + if (percentage < 0 || percentage > 100) throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + + public Money applyTo(Money amount) { + BigDecimal factor = BigDecimal.ONE.subtract(BigDecimal.valueOf(percentage).divide(BigDecimal.valueOf(100))); + return Money.of(amount.amount().multiply(factor), amount.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Money.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Money.java new file mode 100644 index 0000000..cc8beab --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Money.java @@ -0,0 +1,23 @@ +package org.store.orders.domain; + +import java.math.BigDecimal; + +public record Money(BigDecimal amount, String currency) { + public Money { + if (amount == null) throw new IllegalArgumentException("Amount cannot be null"); + if (currency == null || currency.isBlank()) throw new IllegalArgumentException("Currency required"); + } + public static Money of(BigDecimal amount, String currency) { + return new Money(amount, currency); + } + public static Money zero(String currency) { + return new Money(BigDecimal.ZERO, currency); + } + public Money add(Money other) { + if (!currency.equals(other.currency)) throw new IllegalArgumentException("Currency mismatch"); + return new Money(amount.add(other.amount), currency); + } + public boolean isPositive() { + return amount.compareTo(BigDecimal.ZERO) > 0; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java new file mode 100644 index 0000000..5508984 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/Order.java @@ -0,0 +1,86 @@ +package org.store.orders.domain; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class Order { + + private final OrderId id; + private final CustomerId customerId; + private final List lines; + private OrderStatus status; + private final Instant createdAt; + private ShippingAddress shippingAddress; + private DiscountCode discountCode; + + private Order(OrderId id, CustomerId customerId, Instant createdAt) { + this.id = id; + this.customerId = customerId; + this.lines = new ArrayList<>(); + this.status = OrderStatus.DRAFT; + this.createdAt = createdAt; + } + + public static Order create(CustomerId customerId, Instant now) { + return new Order(OrderId.generate(), customerId, now); + } + + public void addLine(String productName, int quantity, Money unitPrice) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Can only add lines to draft orders"); + } + lines.add(new OrderLine(productName, quantity, unitPrice)); + } + + public void applyDiscount(DiscountCode discountCode) { + this.discountCode = discountCode; + } + + public OrderCancelled cancel(Instant now) { + if (status != OrderStatus.PLACED) { + throw new IllegalStateException("Only placed orders can be cancelled"); + } + this.status = OrderStatus.CANCELLED; + return new OrderCancelled(id, now); + } + + public Money discountedTotal() { + Money orderTotal = total(); + if (discountCode == null) { + return orderTotal; + } + return discountCode.applyTo(orderTotal); + } + + public void place() { + if (lines.isEmpty()) { + throw new IllegalStateException("Cannot place an empty order"); + } + this.status = OrderStatus.PLACED; + } + + public void setShippingAddress(ShippingAddress shippingAddress) { + if (status != OrderStatus.DRAFT) { + throw new IllegalStateException("Shipping address can only be set on draft orders"); + } + this.shippingAddress = shippingAddress; + } + + public Money total() { + return lines.stream() + .map(OrderLine::lineTotal) + .reduce(Money.zero("USD"), Money::add); + } + + public OrderId id() { return id; } + public CustomerId customerId() { return customerId; } + public List lines() { return Collections.unmodifiableList(lines); } + public OrderStatus status() { return status; } + public ShippingAddress shippingAddress() { return shippingAddress; } + public DiscountCode discountCode() { return discountCode; } + public boolean isDraft() { return status == OrderStatus.DRAFT; } + public boolean isPlaced() { return status == OrderStatus.PLACED; } + public boolean isCancelled() { return status == OrderStatus.CANCELLED; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java new file mode 100644 index 0000000..764925f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderCancelled.java @@ -0,0 +1,11 @@ +package org.store.orders.domain; + +import java.time.Instant; + +public record OrderCancelled(OrderId orderId, Instant cancelledAt) { + + public OrderCancelled { + if (orderId == null) throw new IllegalArgumentException("Order id is required"); + if (cancelledAt == null) throw new IllegalArgumentException("Cancelled at is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderId.java new file mode 100644 index 0000000..95b4383 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderId.java @@ -0,0 +1,13 @@ +package org.store.orders.domain; + +import java.util.Objects; +import java.util.UUID; + +public record OrderId(UUID value) { + public OrderId { + Objects.requireNonNull(value); + } + public static OrderId generate() { + return new OrderId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderLine.java new file mode 100644 index 0000000..d2bd921 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderLine.java @@ -0,0 +1,12 @@ +package org.store.orders.domain; + +record OrderLine(String productName, int quantity, Money unitPrice) { + + OrderLine { + if (quantity <= 0) throw new IllegalArgumentException("Quantity must be positive"); + } + + Money lineTotal() { + return Money.of(unitPrice.amount().multiply(java.math.BigDecimal.valueOf(quantity)), unitPrice.currency()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderRepository.java new file mode 100644 index 0000000..1f11aa9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderRepository.java @@ -0,0 +1,8 @@ +package org.store.orders.domain; + +import java.util.Optional; + +public interface OrderRepository { + void save(Order order); + Optional findById(OrderId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderStatus.java new file mode 100644 index 0000000..c1c9914 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/OrderStatus.java @@ -0,0 +1,5 @@ +package org.store.orders.domain; + +public enum OrderStatus { + DRAFT, PLACED, CANCELLED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java new file mode 100644 index 0000000..604e7a2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/outputs/src/main/java/org/store/orders/domain/ShippingAddress.java @@ -0,0 +1,10 @@ +package org.store.orders.domain; + +public record ShippingAddress(String street, String city, String postalCode) { + + public ShippingAddress { + if (street == null || street.isBlank()) throw new IllegalArgumentException("Street is required"); + if (city == null || city.isBlank()) throw new IllegalArgumentException("City is required"); + if (postalCode == null || postalCode.isBlank()) throw new IllegalArgumentException("Postal code is required"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/timing.json new file mode 100644 index 0000000..8fe5e7b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/order-delta/without_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 25859, "duration_ms": 76131, "total_duration_seconds": 76.1} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/grading.json new file mode 100644 index 0000000..0ae96d3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-7/payroll-mixed/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": true, + "evidence": "imports EmployeeId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": true, + "evidence": "Payslip package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/ExecutePayrollService.java new file mode 100644 index 0000000..dba5e21 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/ExecutePayrollService.java @@ -0,0 +1,29 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRun; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(this::executeAndSave) + .orElse(left("Payroll run not found")); + } + + private Either executeAndSave(PayrollRun payrollRun) { + return payrollRun.execute() + .peek(event -> payrollRunRepository.save(payrollRun)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollExecuted.java new file mode 100644 index 0000000..3adb3ed --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollExecuted.java @@ -0,0 +1,50 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { + return payrollRunId; + } + + public YearMonth month() { + return month; + } + + public BigDecimal totalNetPay() { + return totalNetPay; + } + + public Instant executedAt() { + return executedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRun.java new file mode 100644 index 0000000..89e5238 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRun.java @@ -0,0 +1,86 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(YearMonth month) { + return new PayrollRun(PayrollRunId.generate(), month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status == PayrollStatus.EXECUTED) { + return left("An executed payroll run cannot be modified"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status == PayrollStatus.EXECUTED) { + return left("A payroll run can only be executed once"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + BigDecimal totalNetPay = payslips.stream() + .map(payslip -> payslip.salary().netPay()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + payslips.forEach(Payslip::markPaid); + this.status = PayrollStatus.EXECUTED; + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { + return id; + } + + public YearMonth month() { + return month; + } + + public List payslips() { + return Collections.unmodifiableList(payslips); + } + + public PayrollStatus status() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunRepository.java new file mode 100644 index 0000000..2cbf9ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollRunRepository.java @@ -0,0 +1,8 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + void save(PayrollRun payrollRun); + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Payslip.java new file mode 100644 index 0000000..2f921f4 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Payslip.java @@ -0,0 +1,49 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.Objects; + +final class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + Objects.requireNonNull(employeeId); + Objects.requireNonNull(salary); + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + EmployeeId employeeId() { + return employeeId; + } + + Salary salary() { + return salary; + } + + boolean isPaid() { + return paid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Salary.java new file mode 100644 index 0000000..9d99650 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/outputs/Salary.java @@ -0,0 +1,49 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + Objects.requireNonNull(grossAmount); + Objects.requireNonNull(deductions); + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { + return grossAmount; + } + + public BigDecimal deductions() { + return deductions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/timing.json new file mode 100644 index 0000000..8b88cbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/with_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 32515, "duration_ms": 95403, "total_duration_seconds": 95.4} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/grading.json new file mode 100644 index 0000000..ae672c0 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-7/payroll-mixed/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": false, + "evidence": "imports EmployeeId: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": false, + "evidence": "Payslip package-private: False" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java new file mode 100644 index 0000000..dba5e21 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java @@ -0,0 +1,29 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRun; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(this::executeAndSave) + .orElse(left("Payroll run not found")); + } + + private Either executeAndSave(PayrollRun payrollRun) { + return payrollRun.execute() + .peek(event -> payrollRunRepository.save(payrollRun)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java new file mode 100644 index 0000000..a7ff173 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class EmployeeId { + + private final UUID value; + + private EmployeeId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static EmployeeId of(UUID value) { + return new EmployeeId(value); + } + + public static EmployeeId generate() { + return new EmployeeId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EmployeeId that = (EmployeeId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java new file mode 100644 index 0000000..471f446 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { return payrollRunId; } + public YearMonth month() { return month; } + public BigDecimal totalNetPay() { return totalNetPay; } + public Instant executedAt() { return executedAt; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java new file mode 100644 index 0000000..07cbf3c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java @@ -0,0 +1,75 @@ +package com.acme.hr.payroll.domain; + +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(YearMonth month) { + return new PayrollRun(PayrollRunId.generate(), month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status == PayrollStatus.EXECUTED) { + return left("An executed payroll run cannot be modified"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status == PayrollStatus.EXECUTED) { + return left("A payroll run can only be executed once"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + this.status = PayrollStatus.EXECUTED; + BigDecimal totalNetPay = BigDecimal.ZERO; + for (Payslip payslip : payslips) { + payslip.markPaid(); + totalNetPay = totalNetPay.add(payslip.salary().netPay()); + } + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { return id; } + public YearMonth month() { return month; } + public List payslips() { return Collections.unmodifiableList(payslips); } + public PayrollStatus status() { return status; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java new file mode 100644 index 0000000..2cbf9ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java @@ -0,0 +1,8 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + void save(PayrollRun payrollRun); + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java new file mode 100644 index 0000000..a29fb69 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java @@ -0,0 +1,45 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; + +public class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + public EmployeeId employeeId() { + return employeeId; + } + + public Salary salary() { + return salary; + } + + public boolean isPaid() { + return paid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java new file mode 100644 index 0000000..aabea3c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java @@ -0,0 +1,50 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + if (deductions.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Deductions cannot be negative"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { + return grossAmount; + } + + public BigDecimal deductions() { + return deductions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/timing.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/timing.json new file mode 100644 index 0000000..d2f4785 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-7/payroll-mixed/without_skill/timing.json @@ -0,0 +1 @@ +{"total_tokens": 27194, "duration_ms": 79755, "total_duration_seconds": 79.8} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..2fbffb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-8/pricing-plain-java/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..c2e1312 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either apply(PriceListId priceListId, ProductId productId, Discount discount) { + PriceList priceList = priceListRepository.findById(priceListId) + .orElse(null); + if (priceList == null) { + return left("Price list not found: " + priceListId); + } + if (!priceList.isActive()) { + return left("Price list is not active"); + } + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/Discount.java new file mode 100644 index 0000000..bba90ec --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/Discount.java @@ -0,0 +1,46 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal discountFraction = BigDecimal.valueOf(percentage).divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP); + BigDecimal discountAmount = price.amount().multiply(discountFraction); + BigDecimal discountedAmount = price.amount().subtract(discountAmount); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + return Money.zero(price.currency()); + } + return Money.of(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..9adc9fc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceEntry.java @@ -0,0 +1,55 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = null; + } + + Money effectivePrice() { + if (discount == null) { + return basePrice; + } + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceList.java new file mode 100644 index 0000000..c3cba5a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceList.java @@ -0,0 +1,95 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + PriceEntry entry = findEntryByProductId(productId); + if (entry == null) { + return left("No price entry found for product: " + productId); + } + entry.applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private PriceEntry findEntryByProductId(ProductId productId) { + for (PriceEntry entry : entries) { + if (entry.productId().equals(productId)) { + return entry; + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..3071cc7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PriceListRepository.java @@ -0,0 +1,8 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PricingService.java new file mode 100644 index 0000000..330b4e8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/with_skill/outputs/PricingService.java @@ -0,0 +1,19 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + if (!priceList.isActive()) { + return Optional.empty(); + } + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst() + .map(PriceEntry::effectivePrice); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..cdf18be --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-8/pricing-plain-java/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": false, + "evidence": "service files: ['PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..63b1607 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,46 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal factor = BigDecimal.valueOf(100 - percentage) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + BigDecimal discountedAmount = price.amount().multiply(factor); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + return Money.zero(price.currency()); + } + return Money.of(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..9704614 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,55 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = null; + } + + public Money effectivePrice() { + if (discount == null) { + return basePrice; + } + return discount.applyTo(basePrice); + } + + public void applyDiscount(Discount discount) { + this.discount = discount; + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..2b3db55 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,93 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entry = findEntry(productId); + if (entry.isEmpty()) { + return left("No price entry found for product: " + productId); + } + entry.get().applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private Optional findEntry(ProductId productId) { + return entries.stream() + .filter(e -> e.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..3a04cad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/pricing-plain-java/without_skill/outputs/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,19 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + if (!priceList.isActive()) { + return Optional.empty(); + } + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .map(PriceEntry::effectivePrice) + .findFirst(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/grading.json new file mode 100644 index 0000000..4c7f692 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-8/shipping-lombok-spring/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..0e9c1a5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..3836a93 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,25 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..14f9707 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (trackingNumber == null) { + throw new IllegalStateException("Cannot dispatch shipment without a tracking number"); + } + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("Shipment is already delivered"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..b8bc97a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,24 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public static ShippingAddress of(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + return new ShippingAddress(street, city, postalCode, country); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..11eab14 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/with_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public static TrackingNumber of(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number cannot be blank"); + } + return new TrackingNumber(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/grading.json new file mode 100644 index 0000000..e00d819 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "shipping", + "output_dir": "iteration-8/shipping-lombok-spring/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "facade_pattern_followed", + "passed": true, + "evidence": "facade files: ['ShippingFacade.java'], public: True" + }, + { + "text": "spring_configuration_present", + "passed": true, + "evidence": "config files: ['ShippingConfiguration.java'], @Bean+@Configuration: True" + }, + { + "text": "lombok_value_for_vos", + "passed": true, + "evidence": "3/3 VOs use @Value" + }, + { + "text": "domain_event_interface_reused", + "passed": true, + "evidence": "imports existing DomainEvent: True, creates new: False" + }, + { + "text": "pending_events_pattern", + "passed": true, + "evidence": "pendingEvents: True, flushEvents: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 8/8: ['Shipment', 'ShipmentId', 'ShippingAddress', 'TrackingNumber', 'ShipmentCreated', 'ShipmentDispatched', 'ShipmentRepository', 'ShipmentStatus']" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "tracking check: True, status check: True" + }, + { + "text": "orderid_reused_not_recreated", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java new file mode 100644 index 0000000..a2fbae1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/CreateShipmentService.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +class CreateShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + CreateShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + ShipmentId create(OrderId orderId, ShippingAddress address) { + Shipment shipment = Shipment.createForOrder(orderId, address); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + return shipment.getId(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java new file mode 100644 index 0000000..5e0cdd8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/DispatchShipmentService.java @@ -0,0 +1,26 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; + +class DispatchShipmentService { + + private final ShipmentRepository shipmentRepository; + private final DomainEventPublisher eventPublisher; + + DispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + this.shipmentRepository = shipmentRepository; + this.eventPublisher = eventPublisher; + } + + void dispatch(ShipmentId shipmentId) { + Shipment shipment = shipmentRepository.findById(shipmentId) + .orElseThrow(() -> new IllegalArgumentException("Shipment not found: " + shipmentId)); + shipment.dispatch(); + shipmentRepository.save(shipment); + shipment.flushEvents().forEach(eventPublisher::publish); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java new file mode 100644 index 0000000..fc09db9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingConfiguration.java @@ -0,0 +1,29 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.DomainEventPublisher; +import com.example.orders.shipping.domain.ShipmentRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class ShippingConfiguration { + + @Bean + ShippingFacade shippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + return new ShippingFacade(createShipmentService, dispatchShipmentService, shipmentRepository); + } + + @Bean + CreateShipmentService createShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new CreateShipmentService(shipmentRepository, eventPublisher); + } + + @Bean + DispatchShipmentService dispatchShipmentService(ShipmentRepository shipmentRepository, + DomainEventPublisher eventPublisher) { + return new DispatchShipmentService(shipmentRepository, eventPublisher); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java new file mode 100644 index 0000000..ec13a3b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/application/ShippingFacade.java @@ -0,0 +1,36 @@ +package com.example.orders.shipping.application; + +import com.example.orders.domain.OrderId; +import com.example.orders.shipping.domain.Shipment; +import com.example.orders.shipping.domain.ShipmentId; +import com.example.orders.shipping.domain.ShipmentRepository; +import com.example.orders.shipping.domain.ShippingAddress; + +import java.util.Optional; + +public class ShippingFacade { + + private final CreateShipmentService createShipmentService; + private final DispatchShipmentService dispatchShipmentService; + private final ShipmentRepository shipmentRepository; + + ShippingFacade(CreateShipmentService createShipmentService, + DispatchShipmentService dispatchShipmentService, + ShipmentRepository shipmentRepository) { + this.createShipmentService = createShipmentService; + this.dispatchShipmentService = dispatchShipmentService; + this.shipmentRepository = shipmentRepository; + } + + public ShipmentId createShipment(OrderId orderId, ShippingAddress address) { + return createShipmentService.create(orderId, address); + } + + public void dispatchShipment(ShipmentId shipmentId) { + dispatchShipmentService.dispatch(shipmentId); + } + + public Optional findShipment(ShipmentId shipmentId) { + return shipmentRepository.findById(shipmentId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java new file mode 100644 index 0000000..90c9f35 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/Shipment.java @@ -0,0 +1,62 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Getter; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Shipment { + + private final ShipmentId id; + private final OrderId orderId; + private final ShippingAddress address; + private TrackingNumber trackingNumber; + private ShipmentStatus status; + private final List pendingEvents = new ArrayList<>(); + + private Shipment(ShipmentId id, OrderId orderId, ShippingAddress address) { + this.id = id; + this.orderId = orderId; + this.address = address; + this.status = ShipmentStatus.CREATED; + } + + public static Shipment createForOrder(OrderId orderId, ShippingAddress address) { + ShipmentId id = ShipmentId.generate(); + Shipment shipment = new Shipment(id, orderId, address); + shipment.pendingEvents.add(new ShipmentCreated(id, orderId, Instant.now())); + return shipment; + } + + public void assignTrackingNumber(TrackingNumber trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public void dispatch() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be dispatched again"); + } + if (trackingNumber == null) { + throw new IllegalStateException("A shipment can only be dispatched if it has a tracking number assigned"); + } + this.status = ShipmentStatus.DISPATCHED; + pendingEvents.add(new ShipmentDispatched(id, trackingNumber, Instant.now())); + } + + public void confirmDelivery() { + if (status == ShipmentStatus.DELIVERED) { + throw new IllegalStateException("A delivered shipment cannot be delivered again"); + } + this.status = ShipmentStatus.DELIVERED; + } + + public List flushEvents() { + List events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java new file mode 100644 index 0000000..f580cda --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentCreated.java @@ -0,0 +1,14 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import com.example.orders.domain.OrderId; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentCreated implements DomainEvent { + ShipmentId shipmentId; + OrderId orderId; + Instant createdAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java new file mode 100644 index 0000000..2017e02 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentDispatched.java @@ -0,0 +1,13 @@ +package com.example.orders.shipping.domain; + +import com.example.orders.domain.DomainEvent; +import lombok.Value; + +import java.time.Instant; + +@Value +public class ShipmentDispatched implements DomainEvent { + ShipmentId shipmentId; + TrackingNumber trackingNumber; + Instant dispatchedAt; +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java new file mode 100644 index 0000000..96183b8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentId.java @@ -0,0 +1,18 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +import java.util.UUID; + +@Value +public class ShipmentId { + UUID value; + + public static ShipmentId generate() { + return new ShipmentId(UUID.randomUUID()); + } + + public static ShipmentId of(UUID value) { + return new ShipmentId(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java new file mode 100644 index 0000000..c0dcf08 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentRepository.java @@ -0,0 +1,8 @@ +package com.example.orders.shipping.domain; + +import java.util.Optional; + +public interface ShipmentRepository { + void save(Shipment shipment); + Optional findById(ShipmentId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java new file mode 100644 index 0000000..b257728 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShipmentStatus.java @@ -0,0 +1,5 @@ +package com.example.orders.shipping.domain; + +public enum ShipmentStatus { + CREATED, DISPATCHED, DELIVERED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java new file mode 100644 index 0000000..97d0b89 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/ShippingAddress.java @@ -0,0 +1,27 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class ShippingAddress { + String street; + String city; + String postalCode; + String country; + + public ShippingAddress(String street, String city, String postalCode, String country) { + if (street == null || street.isBlank()) { + throw new IllegalArgumentException("Street is required"); + } + if (city == null || city.isBlank()) { + throw new IllegalArgumentException("City is required"); + } + if (postalCode == null || postalCode.isBlank()) { + throw new IllegalArgumentException("Postal code is required"); + } + this.street = street; + this.city = city; + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java new file mode 100644 index 0000000..8a4ec4c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/shipping-lombok-spring/without_skill/outputs/com/example/orders/shipping/domain/TrackingNumber.java @@ -0,0 +1,15 @@ +package com.example.orders.shipping.domain; + +import lombok.Value; + +@Value +public class TrackingNumber { + String value; + + public TrackingNumber(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Tracking number value is required"); + } + this.value = value; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/grading.json new file mode 100644 index 0000000..8be05c8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-8/transfers-records-modern/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java new file mode 100644 index 0000000..40f803f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferCommand.java @@ -0,0 +1,9 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java new file mode 100644 index 0000000..cf7d49f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/InitiateTransferHandler.java @@ -0,0 +1,24 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + + public InitiateTransferHandler(TransferRepository transferRepository) { + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/Transfer.java new file mode 100644 index 0000000..8ebf1e9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/Transfer.java @@ -0,0 +1,74 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.isNegative() || amount.value().signum() == 0) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/with_skill/outputs/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/grading.json new file mode 100644 index 0000000..7dcbc9d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "transfers", + "output_dir": "iteration-8/transfers-records-modern/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "transfers package: True" + }, + { + "text": "command_handler_pattern", + "passed": true, + "evidence": "commands: ['InitiateTransferCommand.java'], handlers: ['InitiateTransferHandler.java'], implements: cmd=True, handler=True" + }, + { + "text": "records_for_value_objects", + "passed": true, + "evidence": "TransferId is record: True" + }, + { + "text": "sealed_event_interface", + "passed": true, + "evidence": "sealed Event interface: True" + }, + { + "text": "accountid_reused_not_recreated", + "passed": true, + "evidence": "imports AccountId: True, creates new: False" + }, + { + "text": "amount_reused_not_recreated", + "passed": true, + "evidence": "imports Amount: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['Transfer', 'TransferId', 'TransferStatus', 'TransferInitiated (inner)', 'TransferCompleted (inner)', 'TransferRepository']" + }, + { + "text": "same_account_validation", + "passed": true, + "evidence": "same-account validation: True" + }, + { + "text": "modern_java_features", + "passed": true, + "evidence": "var: True, record: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java new file mode 100644 index 0000000..54a0bb3 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferCommand.java @@ -0,0 +1,10 @@ +package dev.app.banking.transfers; + +import dev.app.banking.shared.Command; + +import java.math.BigDecimal; +import java.util.UUID; + +public record InitiateTransferCommand(UUID sourceAccountId, UUID destinationAccountId, + BigDecimal amount) implements Command { +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java new file mode 100644 index 0000000..cf7d49f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/InitiateTransferHandler.java @@ -0,0 +1,24 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; +import dev.app.banking.shared.CommandHandler; + +public final class InitiateTransferHandler implements CommandHandler { + + private final TransferRepository transferRepository; + + public InitiateTransferHandler(TransferRepository transferRepository) { + this.transferRepository = transferRepository; + } + + @Override + public TransferId handle(InitiateTransferCommand command) { + var sourceAccountId = new AccountId(command.sourceAccountId()); + var destinationAccountId = new AccountId(command.destinationAccountId()); + var amount = Amount.of(command.amount()); + var transfer = Transfer.initiate(sourceAccountId, destinationAccountId, amount); + transferRepository.save(transfer); + return transfer.id(); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java new file mode 100644 index 0000000..04e3130 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/Transfer.java @@ -0,0 +1,78 @@ +package dev.app.banking.transfers; + +import dev.app.banking.accounts.AccountId; +import dev.app.banking.accounts.Amount; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class Transfer { + + sealed interface Event permits TransferInitiated, TransferCompleted {} + record TransferInitiated(TransferId transferId, AccountId sourceAccountId, + AccountId destinationAccountId, Amount amount, + Instant initiatedAt) implements Event {} + record TransferCompleted(TransferId transferId, Instant completedAt) implements Event {} + + private final TransferId id; + private final AccountId sourceAccountId; + private final AccountId destinationAccountId; + private final Amount amount; + private TransferStatus status; + private final Instant initiatedAt; + private final List pendingEvents = new ArrayList<>(); + + private Transfer(TransferId id, AccountId sourceAccountId, AccountId destinationAccountId, + Amount amount, TransferStatus status, Instant initiatedAt) { + this.id = id; + this.sourceAccountId = sourceAccountId; + this.destinationAccountId = destinationAccountId; + this.amount = amount; + this.status = status; + this.initiatedAt = initiatedAt; + } + + public static Transfer initiate(AccountId sourceAccountId, AccountId destinationAccountId, Amount amount) { + if (sourceAccountId.equals(destinationAccountId)) { + throw new IllegalArgumentException("Source and destination accounts must be different"); + } + if (amount.value().compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Transfer amount must be positive"); + } + var id = TransferId.generate(); + var now = Instant.now(); + var transfer = new Transfer(id, sourceAccountId, destinationAccountId, amount, TransferStatus.INITIATED, now); + transfer.pendingEvents.add(new TransferInitiated(id, sourceAccountId, destinationAccountId, amount, now)); + return transfer; + } + + public void complete() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.COMPLETED; + pendingEvents.add(new TransferCompleted(id, Instant.now())); + } + + public void fail() { + if (status != TransferStatus.INITIATED) { + throw new IllegalStateException("A completed transfer cannot be modified"); + } + this.status = TransferStatus.FAILED; + } + + public TransferId id() { return id; } + public AccountId sourceAccountId() { return sourceAccountId; } + public AccountId destinationAccountId() { return destinationAccountId; } + public Amount amount() { return amount; } + public TransferStatus status() { return status; } + public Instant initiatedAt() { return initiatedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java new file mode 100644 index 0000000..5a8d9b2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferId.java @@ -0,0 +1,14 @@ +package dev.app.banking.transfers; + +import java.util.UUID; + +public record TransferId(UUID value) { + + public TransferId { + if (value == null) throw new IllegalArgumentException("TransferId cannot be null"); + } + + public static TransferId generate() { + return new TransferId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java new file mode 100644 index 0000000..829e8ce --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferRepository.java @@ -0,0 +1,8 @@ +package dev.app.banking.transfers; + +import java.util.Optional; + +public interface TransferRepository { + void save(Transfer transfer); + Optional findById(TransferId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java new file mode 100644 index 0000000..d42dd74 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-8/transfers-records-modern/without_skill/outputs/src/main/java/dev/app/banking/transfers/TransferStatus.java @@ -0,0 +1,7 @@ +package dev.app.banking.transfers; + +public enum TransferStatus { + INITIATED, + COMPLETED, + FAILED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/grading.json new file mode 100644 index 0000000..38a64b7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-9/payroll-mixed/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": true, + "evidence": "imports EmployeeId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": true, + "evidence": "Payslip package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java new file mode 100644 index 0000000..dba5e21 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/application/ExecutePayrollService.java @@ -0,0 +1,29 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRun; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(this::executeAndSave) + .orElse(left("Payroll run not found")); + } + + private Either executeAndSave(PayrollRun payrollRun) { + return payrollRun.execute() + .peek(event -> payrollRunRepository.save(payrollRun)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java new file mode 100644 index 0000000..3adb3ed --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollExecuted.java @@ -0,0 +1,50 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { + return payrollRunId; + } + + public YearMonth month() { + return month; + } + + public BigDecimal totalNetPay() { + return totalNetPay; + } + + public Instant executedAt() { + return executedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java new file mode 100644 index 0000000..e74754e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRun.java @@ -0,0 +1,87 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(YearMonth month) { + return new PayrollRun(PayrollRunId.generate(), month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status == PayrollStatus.EXECUTED) { + return left("An executed payroll run cannot be modified"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status == PayrollStatus.EXECUTED) { + return left("A payroll run can only be executed once"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + this.status = PayrollStatus.EXECUTED; + BigDecimal totalNetPay = BigDecimal.ZERO; + for (Payslip payslip : payslips) { + payslip.markPaid(); + totalNetPay = totalNetPay.add(payslip.salary().netPay()); + } + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { + return id; + } + + public YearMonth month() { + return month; + } + + public List payslips() { + return Collections.unmodifiableList(payslips); + } + + public PayrollStatus status() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java new file mode 100644 index 0000000..858e89f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollRunRepository.java @@ -0,0 +1,10 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + + void save(PayrollRun payrollRun); + + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java new file mode 100644 index 0000000..b6637e2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Payslip.java @@ -0,0 +1,47 @@ +package com.acme.hr.payroll.domain; + +import com.acme.hr.employees.EmployeeId; + +import java.util.Objects; + +class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + EmployeeId employeeId() { + return employeeId; + } + + Salary salary() { + return salary; + } + + boolean isPaid() { + return paid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java new file mode 100644 index 0000000..bde64e7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/with_skill/outputs/com/acme/hr/payroll/domain/Salary.java @@ -0,0 +1,47 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { + return grossAmount; + } + + public BigDecimal deductions() { + return deductions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/grading.json new file mode 100644 index 0000000..93e1c1c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "payroll", + "output_dir": "iteration-9/payroll-mixed/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "payroll package: True" + }, + { + "text": "uses_rich_model_not_anemic", + "passed": true, + "evidence": "Either: True, Vavr import: True, no setters: True" + }, + { + "text": "immutable_value_objects", + "passed": true, + "evidence": "final/record: True, no setters: True" + }, + { + "text": "service_per_use_case", + "passed": true, + "evidence": "services: ['ExecutePayrollService.java'], god service: False" + }, + { + "text": "domain_application_layer_split", + "passed": true, + "evidence": "domain pkg: True, application pkg: True" + }, + { + "text": "employeeid_reused", + "passed": false, + "evidence": "imports EmployeeId: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PayrollRun', 'PayrollRunId', 'PayrollStatus', 'Salary', 'Payslip', 'PayrollExecuted', 'PayrollRunRepository']" + }, + { + "text": "payslip_package_private", + "passed": false, + "evidence": "Payslip package-private: False" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, executed guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java new file mode 100644 index 0000000..dba5e21 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/application/ExecutePayrollService.java @@ -0,0 +1,29 @@ +package com.acme.hr.payroll.application; + +import com.acme.hr.payroll.domain.PayrollExecuted; +import com.acme.hr.payroll.domain.PayrollRun; +import com.acme.hr.payroll.domain.PayrollRunId; +import com.acme.hr.payroll.domain.PayrollRunRepository; +import io.vavr.control.Either; + +import static io.vavr.control.Either.left; + +public class ExecutePayrollService { + + private final PayrollRunRepository payrollRunRepository; + + public ExecutePayrollService(PayrollRunRepository payrollRunRepository) { + this.payrollRunRepository = payrollRunRepository; + } + + public Either execute(PayrollRunId payrollRunId) { + return payrollRunRepository.findById(payrollRunId) + .map(this::executeAndSave) + .orElse(left("Payroll run not found")); + } + + private Either executeAndSave(PayrollRun payrollRun) { + return payrollRun.execute() + .peek(event -> payrollRunRepository.save(payrollRun)); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java new file mode 100644 index 0000000..a7ff173 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/EmployeeId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class EmployeeId { + + private final UUID value; + + private EmployeeId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static EmployeeId of(UUID value) { + return new EmployeeId(value); + } + + public static EmployeeId generate() { + return new EmployeeId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EmployeeId that = (EmployeeId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java new file mode 100644 index 0000000..3adb3ed --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollExecuted.java @@ -0,0 +1,50 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.Objects; + +public final class PayrollExecuted { + + private final PayrollRunId payrollRunId; + private final YearMonth month; + private final BigDecimal totalNetPay; + private final Instant executedAt; + + PayrollExecuted(PayrollRunId payrollRunId, YearMonth month, BigDecimal totalNetPay, Instant executedAt) { + this.payrollRunId = payrollRunId; + this.month = month; + this.totalNetPay = totalNetPay; + this.executedAt = executedAt; + } + + public PayrollRunId payrollRunId() { + return payrollRunId; + } + + public YearMonth month() { + return month; + } + + public BigDecimal totalNetPay() { + return totalNetPay; + } + + public Instant executedAt() { + return executedAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollExecuted that = (PayrollExecuted) o; + return Objects.equals(payrollRunId, that.payrollRunId); + } + + @Override + public int hashCode() { + return Objects.hash(payrollRunId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java new file mode 100644 index 0000000..dd3feb6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRun.java @@ -0,0 +1,85 @@ +package com.acme.hr.payroll.domain; + +import io.vavr.control.Either; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PayrollRun { + + private final PayrollRunId id; + private final YearMonth month; + private final List payslips; + private PayrollStatus status; + + private PayrollRun(PayrollRunId id, YearMonth month) { + this.id = id; + this.month = month; + this.payslips = new ArrayList<>(); + this.status = PayrollStatus.DRAFT; + } + + public static PayrollRun create(YearMonth month) { + return new PayrollRun(PayrollRunId.generate(), month); + } + + public Either addPayslip(EmployeeId employeeId, Salary salary) { + if (status != PayrollStatus.DRAFT) { + return left("An executed payroll run cannot be modified"); + } + payslips.add(new Payslip(employeeId, salary)); + return right(null); + } + + public Either execute() { + if (status != PayrollStatus.DRAFT) { + return left("A payroll run can only be executed once"); + } + if (payslips.isEmpty()) { + return left("A payroll run must contain at least one payslip before execution"); + } + payslips.forEach(Payslip::markPaid); + this.status = PayrollStatus.EXECUTED; + BigDecimal totalNetPay = payslips.stream() + .map(payslip -> payslip.salary().netPay()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + return right(new PayrollExecuted(id, month, totalNetPay, Instant.now())); + } + + public PayrollRunId id() { + return id; + } + + public YearMonth month() { + return month; + } + + public List payslips() { + return Collections.unmodifiableList(payslips); + } + + public PayrollStatus status() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRun that = (PayrollRun) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java new file mode 100644 index 0000000..29aa195 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunId.java @@ -0,0 +1,39 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; +import java.util.UUID; + +public final class PayrollRunId { + + private final UUID value; + + private PayrollRunId(UUID value) { + Objects.requireNonNull(value); + this.value = value; + } + + public static PayrollRunId of(UUID value) { + return new PayrollRunId(value); + } + + public static PayrollRunId generate() { + return new PayrollRunId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PayrollRunId that = (PayrollRunId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java new file mode 100644 index 0000000..2cbf9ad --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollRunRepository.java @@ -0,0 +1,8 @@ +package com.acme.hr.payroll.domain; + +import java.util.Optional; + +public interface PayrollRunRepository { + void save(PayrollRun payrollRun); + Optional findById(PayrollRunId id); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java new file mode 100644 index 0000000..7b58370 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/PayrollStatus.java @@ -0,0 +1,6 @@ +package com.acme.hr.payroll.domain; + +public enum PayrollStatus { + DRAFT, + EXECUTED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java new file mode 100644 index 0000000..a29fb69 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Payslip.java @@ -0,0 +1,45 @@ +package com.acme.hr.payroll.domain; + +import java.util.Objects; + +public class Payslip { + + private final EmployeeId employeeId; + private final Salary salary; + private boolean paid; + + Payslip(EmployeeId employeeId, Salary salary) { + this.employeeId = employeeId; + this.salary = salary; + this.paid = false; + } + + void markPaid() { + this.paid = true; + } + + public EmployeeId employeeId() { + return employeeId; + } + + public Salary salary() { + return salary; + } + + public boolean isPaid() { + return paid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Payslip payslip = (Payslip) o; + return Objects.equals(employeeId, payslip.employeeId); + } + + @Override + public int hashCode() { + return Objects.hash(employeeId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java new file mode 100644 index 0000000..bde64e7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/payroll-mixed/without_skill/outputs/src/main/java/com/acme/hr/payroll/domain/Salary.java @@ -0,0 +1,47 @@ +package com.acme.hr.payroll.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public final class Salary { + + private final BigDecimal grossAmount; + private final BigDecimal deductions; + + private Salary(BigDecimal grossAmount, BigDecimal deductions) { + if (grossAmount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Salary must be a positive amount"); + } + this.grossAmount = grossAmount; + this.deductions = deductions; + } + + public static Salary of(BigDecimal grossAmount, BigDecimal deductions) { + return new Salary(grossAmount, deductions); + } + + public BigDecimal netPay() { + return grossAmount.subtract(deductions); + } + + public BigDecimal grossAmount() { + return grossAmount; + } + + public BigDecimal deductions() { + return deductions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Salary salary = (Salary) o; + return Objects.equals(grossAmount, salary.grossAmount) && Objects.equals(deductions, salary.deductions); + } + + @Override + public int hashCode() { + return Objects.hash(grossAmount, deductions); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/grading.json new file mode 100644 index 0000000..493e209 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-9/pricing-plain-java/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": true, + "evidence": "service files: ['ApplyDiscountService.java', 'PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": true, + "evidence": "PriceEntry package-private: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java new file mode 100644 index 0000000..03802a2 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/ApplyDiscountService.java @@ -0,0 +1,26 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.ProductId; + +import static io.vavr.control.Either.left; + +public class ApplyDiscountService { + + private final PriceListRepository priceListRepository; + + public ApplyDiscountService(PriceListRepository priceListRepository) { + this.priceListRepository = priceListRepository; + } + + public Either applyDiscount(ProductId productId, Discount discount) { + PriceList priceList = priceListRepository.findActive() + .orElse(null); + if (priceList == null) { + return left("No active price list found"); + } + Either result = priceList.applyDiscount(productId, discount); + result.peek(event -> priceListRepository.save(priceList)); + return result; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/Discount.java new file mode 100644 index 0000000..a2fd788 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/Discount.java @@ -0,0 +1,47 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal discountFraction = BigDecimal.valueOf(percentage) + .divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP); + BigDecimal discountAmount = price.amount().multiply(discountFraction); + BigDecimal discountedAmount = price.amount().subtract(discountAmount); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + return Money.zero(price.currency()); + } + return Money.of(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceEntry.java new file mode 100644 index 0000000..9adc9fc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceEntry.java @@ -0,0 +1,55 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = null; + } + + Money effectivePrice() { + if (discount == null) { + return basePrice; + } + return discount.applyTo(basePrice); + } + + void applyDiscount(Discount discount) { + this.discount = discount; + } + + ProductId productId() { + return productId; + } + + Money basePrice() { + return basePrice; + } + + Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceList.java new file mode 100644 index 0000000..b591dbc --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceList.java @@ -0,0 +1,95 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + PriceEntry entry = findEntry(productId); + if (entry == null) { + return left("No price entry found for product: " + productId); + } + entry.applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + private PriceEntry findEntry(ProductId productId) { + for (PriceEntry entry : entries) { + if (entry.productId().equals(productId)) { + return entry; + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListRepository.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListRepository.java new file mode 100644 index 0000000..c14b2fb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PriceListRepository.java @@ -0,0 +1,9 @@ +package pl.shop.catalog.pricing; + +import java.util.Optional; + +public interface PriceListRepository { + void save(PriceList priceList); + Optional findById(PriceListId id); + Optional findActive(); +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PricingService.java new file mode 100644 index 0000000..330b4e8 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/with_skill/outputs/PricingService.java @@ -0,0 +1,19 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + if (!priceList.isActive()) { + return Optional.empty(); + } + return priceList.entries().stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst() + .map(PriceEntry::effectivePrice); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/grading.json new file mode 100644 index 0000000..2e84d4a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "pricing", + "output_dir": "iteration-9/pricing-plain-java/without_skill/outputs", + "pass_rate": "7/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "pricing package: True" + }, + { + "text": "service_per_use_case_pattern", + "passed": false, + "evidence": "service files: ['PricingService.java']" + }, + { + "text": "vavr_either_returns", + "passed": true, + "evidence": "Either returns: True, Vavr import: True" + }, + { + "text": "no_lombok_used", + "passed": true, + "evidence": "lombok imports found: False" + }, + { + "text": "money_reused_not_recreated", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "productid_reused_not_recreated", + "passed": true, + "evidence": "imports ProductId: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 7/7: ['PriceList', 'PriceListId', 'PriceEntry', 'Discount', 'PricingService', 'DiscountApplied', 'PriceListActivated']" + }, + { + "text": "discount_percentage_validation", + "passed": true, + "evidence": "validation in Discount: True" + }, + { + "text": "package_private_internals", + "passed": false, + "evidence": "PriceEntry package-private: False" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java new file mode 100644 index 0000000..8cf7d9c --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/Discount.java @@ -0,0 +1,46 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +public final class Discount { + + private final int percentage; + + public Discount(int percentage) { + if (percentage < 0 || percentage > 100) { + throw new IllegalArgumentException("Discount percentage must be between 0 and 100"); + } + this.percentage = percentage; + } + + public Money applyTo(Money price) { + BigDecimal discountFactor = BigDecimal.valueOf(100 - percentage) + .divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP); + BigDecimal discountedAmount = price.amount().multiply(discountFactor); + if (discountedAmount.compareTo(BigDecimal.ZERO) < 0) { + return Money.zero(price.currency()); + } + return Money.of(discountedAmount, price.currency()); + } + + public int percentage() { + return percentage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Discount discount = (Discount) o; + return percentage == discount.percentage; + } + + @Override + public int hashCode() { + return Objects.hash(percentage); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java new file mode 100644 index 0000000..e2e3bbb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/DiscountApplied.java @@ -0,0 +1,45 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class DiscountApplied { + + private final PriceListId priceListId; + private final ProductId productId; + private final Discount discount; + + DiscountApplied(PriceListId priceListId, ProductId productId, Discount discount) { + this.priceListId = priceListId; + this.productId = productId; + this.discount = discount; + } + + public PriceListId priceListId() { + return priceListId; + } + + public ProductId productId() { + return productId; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountApplied that = (DiscountApplied) o; + return Objects.equals(priceListId, that.priceListId) + && Objects.equals(productId, that.productId) + && Objects.equals(discount, that.discount); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId, productId, discount); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java new file mode 100644 index 0000000..9704614 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceEntry.java @@ -0,0 +1,55 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Objects; + +public class PriceEntry { + + private final ProductId productId; + private final Money basePrice; + private Discount discount; + + PriceEntry(ProductId productId, Money basePrice) { + this.productId = productId; + this.basePrice = basePrice; + this.discount = null; + } + + public Money effectivePrice() { + if (discount == null) { + return basePrice; + } + return discount.applyTo(basePrice); + } + + public void applyDiscount(Discount discount) { + this.discount = discount; + } + + public ProductId productId() { + return productId; + } + + public Money basePrice() { + return basePrice; + } + + public Discount discount() { + return discount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceEntry that = (PriceEntry) o; + return Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java new file mode 100644 index 0000000..b29afa9 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceList.java @@ -0,0 +1,93 @@ +package pl.shop.catalog.pricing; + +import io.vavr.control.Either; +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.vavr.control.Either.left; +import static io.vavr.control.Either.right; + +public class PriceList { + + private final PriceListId id; + private final String name; + private final List entries; + private boolean active; + + PriceList(PriceListId id, String name) { + this.id = id; + this.name = name; + this.entries = new ArrayList<>(); + this.active = false; + } + + public static PriceList create(String name) { + return new PriceList(PriceListId.newOne(), name); + } + + public Either activate() { + if (entries.isEmpty()) { + return left("A price list must have at least one price entry"); + } + this.active = true; + return right(new PriceListActivated(id)); + } + + public Either addEntry(ProductId productId, Money basePrice) { + if (!basePrice.isPositive()) { + return left("Base price must be positive"); + } + entries.add(new PriceEntry(productId, basePrice)); + return right(null); + } + + public Either applyDiscount(ProductId productId, Discount discount) { + Optional entry = findEntryByProductId(productId); + if (entry.isEmpty()) { + return left("No price entry found for product: " + productId); + } + entry.get().applyDiscount(discount); + return right(new DiscountApplied(id, productId, discount)); + } + + public PriceListId id() { + return id; + } + + public String name() { + return name; + } + + public List entries() { + return Collections.unmodifiableList(entries); + } + + public boolean isActive() { + return active; + } + + Optional findEntryByProductId(ProductId productId) { + return entries.stream() + .filter(entry -> entry.productId().equals(productId)) + .findFirst(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceList priceList = (PriceList) o; + return Objects.equals(id, priceList.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java new file mode 100644 index 0000000..593f84f --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListActivated.java @@ -0,0 +1,29 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; + +public class PriceListActivated { + + private final PriceListId priceListId; + + PriceListActivated(PriceListId priceListId) { + this.priceListId = priceListId; + } + + public PriceListId priceListId() { + return priceListId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListActivated that = (PriceListActivated) o; + return Objects.equals(priceListId, that.priceListId); + } + + @Override + public int hashCode() { + return Objects.hash(priceListId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java new file mode 100644 index 0000000..6052246 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PriceListId.java @@ -0,0 +1,35 @@ +package pl.shop.catalog.pricing; + +import java.util.Objects; +import java.util.UUID; + +public final class PriceListId { + + private final UUID value; + + public PriceListId(UUID value) { + Objects.requireNonNull(value, "PriceListId cannot be null"); + this.value = value; + } + + public static PriceListId newOne() { + return new PriceListId(UUID.randomUUID()); + } + + public UUID value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PriceListId that = (PriceListId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java new file mode 100644 index 0000000..d5320dd --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/pricing-plain-java/without_skill/outputs/src/main/java/pl/shop/catalog/pricing/PricingService.java @@ -0,0 +1,17 @@ +package pl.shop.catalog.pricing; + +import pl.shop.catalog.Money; +import pl.shop.catalog.ProductId; + +import java.util.Optional; + +public class PricingService { + + public Optional calculatePrice(PriceList priceList, ProductId productId) { + if (!priceList.isActive()) { + return Optional.empty(); + } + return priceList.findEntryByProductId(productId) + .map(PriceEntry::effectivePrice); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/grading.json new file mode 100644 index 0000000..3e669b4 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-9/receiving-unusual/with_skill/outputs", + "pass_rate": "8/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "factory_as_inner_class", + "passed": false, + "evidence": "nested Factory in ReceivingNote: False" + }, + { + "text": "sku_reused", + "passed": true, + "evidence": "imports Sku: True, creates new: False" + }, + { + "text": "quantity_reused", + "passed": true, + "evidence": "imports Quantity: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java new file mode 100644 index 0000000..25b0a9b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize_(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivedLine.java new file mode 100644 index 0000000..03b0951 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivedLine.java @@ -0,0 +1,25 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +public final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + public Quantity discrepancy() { + return new Quantity(expectedQuantity.value() - actualQuantity.value()); + } + + public Sku sku() { return sku; } + public Quantity expectedQuantity() { return expectedQuantity; } + public Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNote.java new file mode 100644 index 0000000..273ee97 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNote.java @@ -0,0 +1,67 @@ +package io.proj.warehouse.receiving; + +import io.proj.warehouse.inventory.Quantity; +import io.proj.warehouse.inventory.Sku; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.Open; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity quantity) { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (quantity.value() < 0) { + throw new IllegalArgumentException("Received quantity must be non-negative"); + } + lines.add(new ReceivedLine(sku, quantity, quantity)); + } + + public ReceivingEvents.ReceivingFinalized finalize_() { + if (status == ReceivingStatus.Finalized) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.Finalized; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return Collections.unmodifiableList(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingStatus.java new file mode 100644 index 0000000..8b35caa --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/with_skill/outputs/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + Open, + Finalized +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/grading.json new file mode 100644 index 0000000..9dfedfb --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "receiving", + "output_dir": "iteration-9/receiving-unusual/without_skill/outputs", + "pass_rate": "6/9", + "expectations": [ + { + "text": "correct_package_placement", + "passed": true, + "evidence": "receiving package: True" + }, + { + "text": "sealed_events_in_separate_file", + "passed": true, + "evidence": "event files: ['ReceivingEvents.java'], sealed: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "nested Repository interface in ReceivingNote: True" + }, + { + "text": "factory_as_inner_class", + "passed": false, + "evidence": "nested Factory in ReceivingNote: False" + }, + { + "text": "sku_reused", + "passed": false, + "evidence": "imports Sku: False, creates new: True" + }, + { + "text": "quantity_reused", + "passed": false, + "evidence": "imports Quantity: False, creates new: True" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReceivingNote', 'ReceivingNoteId', 'ReceivedLine', 'ReceivingStatus', 'ReceivingFinalized', 'FinalizeReceivingHandler']" + }, + { + "text": "received_line_package_private", + "passed": true, + "evidence": "ReceivedLine package-private: True" + }, + { + "text": "business_rules_enforced", + "passed": true, + "evidence": "empty check: True, finalized guard: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/FinalizeReceivingHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/FinalizeReceivingHandler.java new file mode 100644 index 0000000..9b5e424 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/FinalizeReceivingHandler.java @@ -0,0 +1,17 @@ +package io.proj.warehouse.receiving; + +public final class FinalizeReceivingHandler { + + private final ReceivingNote.Repository repository; + + public FinalizeReceivingHandler(ReceivingNote.Repository repository) { + this.repository = repository; + } + + public ReceivingEvents.ReceivingFinalized handle(ReceivingNoteId receivingNoteId) { + var note = repository.findById(receivingNoteId); + var event = note.finalize(); + repository.save(note); + return event; + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Quantity.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Quantity.java new file mode 100644 index 0000000..0b1f019 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Quantity.java @@ -0,0 +1,11 @@ +package io.proj.warehouse.receiving; + +public record Quantity(int value) { + public Quantity { + if (value < 0) throw new IllegalArgumentException("Quantity cannot be negative"); + } + + public Quantity subtract(Quantity other) { + return new Quantity(this.value - other.value); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivedLine.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivedLine.java new file mode 100644 index 0000000..4577ae7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivedLine.java @@ -0,0 +1,22 @@ +package io.proj.warehouse.receiving; + +public final class ReceivedLine { + + private final Sku sku; + private final Quantity expectedQuantity; + private final Quantity actualQuantity; + + public ReceivedLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + this.sku = sku; + this.expectedQuantity = expectedQuantity; + this.actualQuantity = actualQuantity; + } + + public Quantity discrepancy() { + return expectedQuantity.subtract(actualQuantity); + } + + public Sku sku() { return sku; } + public Quantity expectedQuantity() { return expectedQuantity; } + public Quantity actualQuantity() { return actualQuantity; } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingEvents.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingEvents.java new file mode 100644 index 0000000..1358151 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingEvents.java @@ -0,0 +1,8 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; + +public sealed interface ReceivingEvents { + + record ReceivingFinalized(ReceivingNoteId receivingNoteId, int lineCount, Instant finalizedAt) implements ReceivingEvents {} +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNote.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNote.java new file mode 100644 index 0000000..4edabc7 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNote.java @@ -0,0 +1,63 @@ +package io.proj.warehouse.receiving; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ReceivingNote { + + private final ReceivingNoteId id; + private final List lines = new ArrayList<>(); + private ReceivingStatus status; + private final Instant receivedAt; + private final List pendingEvents = new ArrayList<>(); + + private ReceivingNote(ReceivingNoteId id, Instant receivedAt) { + this.id = id; + this.status = ReceivingStatus.OPEN; + this.receivedAt = receivedAt; + } + + public static ReceivingNote create() { + return new ReceivingNote(ReceivingNoteId.generate(), Instant.now()); + } + + public void addLine(Sku sku, Quantity expectedQuantity, Quantity actualQuantity) { + ensureNotFinalized(); + lines.add(new ReceivedLine(sku, expectedQuantity, actualQuantity)); + } + + public ReceivingEvents.ReceivingFinalized finalize() { + ensureNotFinalized(); + if (lines.isEmpty()) { + throw new IllegalStateException("A receiving note must have at least one line before it can be finalized"); + } + this.status = ReceivingStatus.FINALIZED; + var event = new ReceivingEvents.ReceivingFinalized(id, lines.size(), Instant.now()); + pendingEvents.add(event); + return event; + } + + public ReceivingNoteId id() { return id; } + public List lines() { return Collections.unmodifiableList(lines); } + public ReceivingStatus status() { return status; } + public Instant receivedAt() { return receivedAt; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + private void ensureNotFinalized() { + if (status == ReceivingStatus.FINALIZED) { + throw new IllegalStateException("A finalized receiving note cannot be modified"); + } + } + + public interface Repository { + void save(ReceivingNote note); + ReceivingNote findById(ReceivingNoteId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNoteId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNoteId.java new file mode 100644 index 0000000..e244636 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingNoteId.java @@ -0,0 +1,14 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; +import java.util.UUID; + +public record ReceivingNoteId(UUID value) { + public ReceivingNoteId { + Objects.requireNonNull(value); + } + + public static ReceivingNoteId generate() { + return new ReceivingNoteId(UUID.randomUUID()); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingStatus.java new file mode 100644 index 0000000..ddc4d7a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/ReceivingStatus.java @@ -0,0 +1,6 @@ +package io.proj.warehouse.receiving; + +public enum ReceivingStatus { + OPEN, + FINALIZED +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Sku.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Sku.java new file mode 100644 index 0000000..4e5f19e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/receiving-unusual/without_skill/outputs/Sku.java @@ -0,0 +1,10 @@ +package io.proj.warehouse.receiving; + +import java.util.Objects; + +public record Sku(String value) { + public Sku { + Objects.requireNonNull(value); + if (value.isBlank()) throw new IllegalArgumentException("SKU cannot be blank"); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/grading.json new file mode 100644 index 0000000..9925ce6 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "returns", + "output_dir": "iteration-9/returns-cross-module/with_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "return_request_in_sales_package", + "passed": true, + "evidence": "sales.returns package: True" + }, + { + "text": "return_receipt_in_fulfillment_package", + "passed": true, + "evidence": "fulfillment package: True" + }, + { + "text": "sealed_events_pattern", + "passed": true, + "evidence": "ReturnRequest events: True, ReturnReceipt events: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "ReturnRequest.Repository: True, ReturnReceipt.Repository: True" + }, + { + "text": "orderid_reused_from_shared", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + }, + { + "text": "money_reused_from_shared", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReturnRequest', 'ReturnRequestId', 'ReturnRequested', 'ReturnReceipt', 'ReturnReceiptId', 'ReturnReceived']" + }, + { + "text": "cross_module_reference", + "passed": true, + "evidence": "ReturnReceipt references ReturnRequestId: True" + }, + { + "text": "return_status_present", + "passed": true, + "evidence": "ReturnStatus: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java new file mode 100644 index 0000000..d2fc247 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java @@ -0,0 +1,53 @@ +package net.ecom.fulfillment.returnprocessing; + +import net.ecom.sales.returns.ReturnRequestId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReturnReceipt { + + public sealed interface Event permits ReturnReceived {} + public record ReturnReceived(ReturnReceiptId returnReceiptId, ReturnRequestId returnRequestId, Instant receivedAt) implements Event {} + + private final ReturnReceiptId id; + private final ReturnRequestId returnRequestId; + private final Instant receivedAt; + private boolean inspected = false; + private final List pendingEvents = new ArrayList<>(); + + private ReturnReceipt(ReturnReceiptId id, ReturnRequestId returnRequestId, Instant receivedAt) { + this.id = id; + this.returnRequestId = returnRequestId; + this.receivedAt = receivedAt; + } + + public static ReturnReceipt receive(ReturnRequestId returnRequestId) { + var id = ReturnReceiptId.generate(); + var receivedAt = Instant.now(); + var receipt = new ReturnReceipt(id, returnRequestId, receivedAt); + receipt.pendingEvents.add(new ReturnReceived(id, returnRequestId, receivedAt)); + return receipt; + } + + public void inspect() { + this.inspected = true; + } + + public ReturnReceiptId id() { return id; } + public ReturnRequestId returnRequestId() { return returnRequestId; } + public Instant receivedAt() { return receivedAt; } + public boolean inspected() { return inspected; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnReceipt returnReceipt); + ReturnReceipt findById(ReturnReceiptId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java new file mode 100644 index 0000000..693765a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java @@ -0,0 +1,9 @@ +package net.ecom.fulfillment.returnprocessing; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnReceiptId(UUID value) { + public ReturnReceiptId { Objects.requireNonNull(value); } + public static ReturnReceiptId generate() { return new ReturnReceiptId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java new file mode 100644 index 0000000..9ee10a1 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequest.java @@ -0,0 +1,58 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +import java.util.ArrayList; +import java.util.List; + +public final class ReturnRequest { + + public sealed interface Event permits ReturnRequested {} + public record ReturnRequested(ReturnRequestId returnRequestId, OrderId orderId, Money refundAmount) implements Event {} + + private final ReturnRequestId id; + private final OrderId orderId; + private final String reason; + private final Money refundAmount; + private ReturnStatus status; + private final List pendingEvents = new ArrayList<>(); + + private ReturnRequest(ReturnRequestId id, OrderId orderId, String reason, Money refundAmount) { + this.id = id; + this.orderId = orderId; + this.reason = reason; + this.refundAmount = refundAmount; + this.status = ReturnStatus.SUBMITTED; + } + + public static ReturnRequest submit(OrderId orderId, Money refundAmount, String reason) { + var request = new ReturnRequest(ReturnRequestId.generate(), orderId, reason, refundAmount); + request.pendingEvents.add(new ReturnRequested(request.id, orderId, refundAmount)); + return request; + } + + public void approve() { + if (status == ReturnStatus.APPROVED) { + throw new IllegalStateException("An approved return cannot be modified"); + } + this.status = ReturnStatus.APPROVED; + } + + public ReturnRequestId id() { return id; } + public OrderId orderId() { return orderId; } + public String reason() { return reason; } + public Money refundAmount() { return refundAmount; } + public ReturnStatus status() { return status; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnRequest returnRequest); + ReturnRequest findById(ReturnRequestId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequestId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequestId.java new file mode 100644 index 0000000..f87c93d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnRequestId.java @@ -0,0 +1,9 @@ +package net.ecom.sales.returns; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnRequestId(UUID value) { + public ReturnRequestId { Objects.requireNonNull(value); } + public static ReturnRequestId generate() { return new ReturnRequestId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnStatus.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnStatus.java new file mode 100644 index 0000000..dfc5093 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/ReturnStatus.java @@ -0,0 +1,3 @@ +package net.ecom.sales.returns; + +enum ReturnStatus { SUBMITTED, APPROVED } diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java new file mode 100644 index 0000000..0943b0a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/with_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java @@ -0,0 +1,25 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.orders.Order; +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +public final class SubmitReturnHandler { + + private final Order.Repository orderRepository; + private final ReturnRequest.Repository returnRequestRepository; + + public SubmitReturnHandler(Order.Repository orderRepository, ReturnRequest.Repository returnRequestRepository) { + this.orderRepository = orderRepository; + this.returnRequestRepository = returnRequestRepository; + } + + public void handle(OrderId orderId, Money refundAmount, String reason) { + var order = orderRepository.findById(orderId); + if (!order.isPaid()) { + throw new IllegalStateException("Only paid orders can be returned"); + } + var returnRequest = ReturnRequest.submit(orderId, refundAmount, reason); + returnRequestRepository.save(returnRequest); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/grading.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/grading.json new file mode 100644 index 0000000..fb68df5 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/grading.json @@ -0,0 +1,52 @@ +{ + "eval_name": "returns", + "output_dir": "iteration-9/returns-cross-module/without_skill/outputs", + "pass_rate": "9/9", + "expectations": [ + { + "text": "return_request_in_sales_package", + "passed": true, + "evidence": "sales.returns package: True" + }, + { + "text": "return_receipt_in_fulfillment_package", + "passed": true, + "evidence": "fulfillment package: True" + }, + { + "text": "sealed_events_pattern", + "passed": true, + "evidence": "ReturnRequest events: True, ReturnReceipt events: True" + }, + { + "text": "repository_as_nested_interface", + "passed": true, + "evidence": "ReturnRequest.Repository: True, ReturnReceipt.Repository: True" + }, + { + "text": "orderid_reused_from_shared", + "passed": true, + "evidence": "imports OrderId: True, creates new: False" + }, + { + "text": "money_reused_from_shared", + "passed": true, + "evidence": "imports Money: True, creates new: False" + }, + { + "text": "all_building_blocks_present", + "passed": true, + "evidence": "found 6/6: ['ReturnRequest', 'ReturnRequestId', 'ReturnRequested', 'ReturnReceipt', 'ReturnReceiptId', 'ReturnReceived']" + }, + { + "text": "cross_module_reference", + "passed": true, + "evidence": "ReturnReceipt references ReturnRequestId: True" + }, + { + "text": "return_status_present", + "passed": true, + "evidence": "ReturnStatus: True" + } + ] +} \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java new file mode 100644 index 0000000..afa529e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceipt.java @@ -0,0 +1,54 @@ +package net.ecom.fulfillment.returnprocessing; + +import net.ecom.sales.shared.ReturnRequestId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class ReturnReceipt { + + public sealed interface Event permits ReturnReceived {} + public record ReturnReceived(ReturnReceiptId returnReceiptId, ReturnRequestId returnRequestId, Instant receivedAt) implements Event {} + + private final ReturnReceiptId id; + private final ReturnRequestId returnRequestId; + private Instant receivedAt; + private boolean inspected = false; + private final List pendingEvents = new ArrayList<>(); + + private ReturnReceipt(ReturnReceiptId id, ReturnRequestId returnRequestId, Instant receivedAt) { + this.id = id; + this.returnRequestId = returnRequestId; + this.receivedAt = receivedAt; + } + + public static ReturnReceipt receive(ReturnRequestId returnRequestId) { + var id = ReturnReceiptId.generate(); + var now = Instant.now(); + var receipt = new ReturnReceipt(id, returnRequestId, now); + receipt.pendingEvents.add(new ReturnReceived(id, returnRequestId, now)); + return receipt; + } + + public void inspect() { + if (inspected) throw new IllegalStateException("Already inspected"); + this.inspected = true; + } + + public ReturnReceiptId id() { return id; } + public ReturnRequestId returnRequestId() { return returnRequestId; } + public Instant receivedAt() { return receivedAt; } + public boolean isInspected() { return inspected; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnReceipt returnReceipt); + ReturnReceipt findByReturnRequestId(ReturnRequestId returnRequestId); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java new file mode 100644 index 0000000..693765a --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/fulfillment/returnprocessing/ReturnReceiptId.java @@ -0,0 +1,9 @@ +package net.ecom.fulfillment.returnprocessing; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnReceiptId(UUID value) { + public ReturnReceiptId { Objects.requireNonNull(value); } + public static ReturnReceiptId generate() { return new ReturnReceiptId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java new file mode 100644 index 0000000..5524b46 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/ReturnRequest.java @@ -0,0 +1,59 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; +import net.ecom.sales.shared.ReturnRequestId; + +import java.util.ArrayList; +import java.util.List; + +public final class ReturnRequest { + + public sealed interface Event permits ReturnRequested {} + public record ReturnRequested(ReturnRequestId returnRequestId, OrderId orderId, Money refundAmount) implements Event {} + + enum ReturnStatus { SUBMITTED, APPROVED } + + private final ReturnRequestId id; + private final OrderId orderId; + private final String reason; + private final Money refundAmount; + private ReturnStatus status; + private final List pendingEvents = new ArrayList<>(); + + private ReturnRequest(ReturnRequestId id, OrderId orderId, String reason, Money refundAmount) { + this.id = id; + this.orderId = orderId; + this.reason = reason; + this.refundAmount = refundAmount; + this.status = ReturnStatus.SUBMITTED; + } + + public static ReturnRequest submit(OrderId orderId, Money orderTotal, String reason) { + var request = new ReturnRequest(ReturnRequestId.generate(), orderId, reason, orderTotal); + request.pendingEvents.add(new ReturnRequested(request.id, orderId, orderTotal)); + return request; + } + + public void approve() { + if (status != ReturnStatus.SUBMITTED) throw new IllegalStateException("An approved return cannot be modified"); + this.status = ReturnStatus.APPROVED; + } + + public ReturnRequestId id() { return id; } + public OrderId orderId() { return orderId; } + public String reason() { return reason; } + public Money refundAmount() { return refundAmount; } + public boolean isApproved() { return status == ReturnStatus.APPROVED; } + + public List flushEvents() { + var events = List.copyOf(pendingEvents); + pendingEvents.clear(); + return events; + } + + public interface Repository { + void save(ReturnRequest returnRequest); + ReturnRequest findById(ReturnRequestId id); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java new file mode 100644 index 0000000..7503c8b --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/returns/SubmitReturnHandler.java @@ -0,0 +1,25 @@ +package net.ecom.sales.returns; + +import net.ecom.sales.orders.Order; +import net.ecom.sales.shared.Money; +import net.ecom.sales.shared.OrderId; + +public final class SubmitReturnHandler { + + private final Order.Repository orderRepository; + private final ReturnRequest.Repository returnRequestRepository; + + public SubmitReturnHandler(Order.Repository orderRepository, ReturnRequest.Repository returnRequestRepository) { + this.orderRepository = orderRepository; + this.returnRequestRepository = returnRequestRepository; + } + + public void handle(OrderId orderId, Money orderTotal, String reason) { + var order = orderRepository.findById(orderId); + if (order == null) throw new IllegalArgumentException("Order not found"); + if (!order.isPaid()) throw new IllegalStateException("Only paid orders can be returned"); + + var returnRequest = ReturnRequest.submit(orderId, orderTotal, reason); + returnRequestRepository.save(returnRequest); + } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java new file mode 100644 index 0000000..ed0585d --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/iteration-9/returns-cross-module/without_skill/outputs/net/ecom/sales/shared/ReturnRequestId.java @@ -0,0 +1,9 @@ +package net.ecom.sales.shared; + +import java.util.Objects; +import java.util.UUID; + +public record ReturnRequestId(UUID value) { + public ReturnRequestId { Objects.requireNonNull(value); } + public static ReturnRequestId generate() { return new ReturnRequestId(UUID.randomUUID()); } +} diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval-exported.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval-exported.json new file mode 100644 index 0000000..70b3f93 --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval-exported.json @@ -0,0 +1,82 @@ +[ + { + "query": "I have a design-doc.json that describes our new Invoicing bounded context with aggregates, value objects, and domain events. Can you implement the Java classes for it? The project is a Spring Boot app at src/main/java/com/acme/billing/", + "should_trigger": true + }, + { + "query": "hey so we just finished the design workshop and the output is this designdoc json file at docs/design/membership-design.json - it has all the building blocks for the membership module. need you to create the java domain model from it, our project uses plain java with vavr for error handling", + "should_trigger": true + }, + { + "query": "Take the DesignDoc at contracts/order-fulfillment.json and implement all the building blocks described there into our existing Java codebase. Some of the types already exist — check before creating duplicates. Project root is services/fulfillment/", + "should_trigger": true + }, + { + "query": "we need to generate java code from this design document json. it describes a Warehouse module with aggregates like StockItem and ReceivingNote, plus domain events and repositories. the json follows the DesignDoc schema with boundedContexts and buildingBlocks", + "should_trigger": true + }, + { + "query": "I've got a design doc JSON (the one with actors, businessGoals, buildingBlocks, useCases etc.) for a new Payment processing module. Need to implement it in Java — the project uses records for VOs and sealed interfaces for events. Can you read the JSON and create all the domain classes?", + "should_trigger": true + }, + { + "query": "Please implement this DesignDoc JSON into my Java project. The JSON has building blocks of types aggregate, entity, value_object, domain_event, domain_service, application_service, and repository. Project at /Users/me/projects/ecommerce/", + "should_trigger": true + }, + { + "query": "I updated the design document with new behaviours on the Order aggregate and a new ShippingAddress value object. Can you sync the Java code to match what's in the updated designdoc json? Some classes need modification, others are new.", + "should_trigger": true + }, + { + "query": "zaimplementuj design doc json do mojego projektu java — plik jest w contracts/design-doc.json, projekt w src/. JSON zawiera boundedContexts, buildingBlocks, useCases i rules", + "should_trigger": true + }, + { + "query": "our architect produced a DesignDoc JSON with the full domain model for the HR system — 3 bounded contexts, 15 building blocks, use cases with scenarios. I need all of this turned into Java classes following our existing project conventions at hr-service/src/main/java/", + "should_trigger": true + }, + { + "query": "the team designed a new Claims module and exported it as a DesignDoc JSON. It references existing types from the Policy module (PolicyId, Premium). Need to implement the Java code, making sure to import existing types instead of recreating them", + "should_trigger": true + }, + { + "query": "Can you write a REST API in Spring Boot for managing users? I need endpoints for CRUD operations with validation and proper error handling", + "should_trigger": false + }, + { + "query": "I need to refactor this Java service class — it's 500 lines long and does too many things. Can you help me split it into smaller classes following single responsibility principle?", + "should_trigger": false + }, + { + "query": "help me write unit tests for my Order aggregate using JUnit 5 and Mockito. The aggregate is at src/main/java/com/shop/orders/Order.java", + "should_trigger": false + }, + { + "query": "I want to create a design document for our new feature. We need to describe the domain model, bounded contexts, and building blocks. Can you help me write the design doc?", + "should_trigger": false + }, + { + "query": "can you review this java code and tell me if it follows DDD patterns correctly? I'm not sure if my aggregate boundaries are right", + "should_trigger": false + }, + { + "query": "I need to set up a new Spring Boot project with Java 21, Maven, and the usual dependencies (web, jpa, lombok). Can you create the project structure?", + "should_trigger": false + }, + { + "query": "convert this Python dataclass model to Java records — I have a User class with name, email, and created_at fields", + "should_trigger": false + }, + { + "query": "I have a JSON schema file that describes our API request/response models. Can you generate Java DTOs from it using Jackson annotations?", + "should_trigger": false + }, + { + "query": "we're migrating from Java 11 to Java 21 — can you update our value objects from final classes to records and our domain events to use sealed interfaces?", + "should_trigger": false + }, + { + "query": "implement the database migration scripts for our new Order tables based on the domain model in src/main/java/com/shop/orders/", + "should_trigger": false + } +] \ No newline at end of file diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval.json b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval.json new file mode 100644 index 0000000..2f4929e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/implement-design-doc-java-workspace/trigger-eval.json @@ -0,0 +1,22 @@ +[ + {"query": "I have a design-doc.json that describes our new Invoicing bounded context with aggregates, value objects, and domain events. Can you implement the Java classes for it? The project is a Spring Boot app at src/main/java/com/acme/billing/", "should_trigger": true}, + {"query": "hey so we just finished the design workshop and the output is this designdoc json file at docs/design/membership-design.json - it has all the building blocks for the membership module. need you to create the java domain model from it, our project uses plain java with vavr for error handling", "should_trigger": true}, + {"query": "Take the DesignDoc at contracts/order-fulfillment.json and implement all the building blocks described there into our existing Java codebase. Some of the types already exist — check before creating duplicates. Project root is services/fulfillment/", "should_trigger": true}, + {"query": "we need to generate java code from this design document json. it describes a Warehouse module with aggregates like StockItem and ReceivingNote, plus domain events and repositories. the json follows the DesignDoc schema with boundedContexts and buildingBlocks", "should_trigger": true}, + {"query": "I've got a design doc JSON (the one with actors, businessGoals, buildingBlocks, useCases etc.) for a new Payment processing module. Need to implement it in Java — the project uses records for VOs and sealed interfaces for events. Can you read the JSON and create all the domain classes?", "should_trigger": true}, + {"query": "Please implement this DesignDoc JSON into my Java project. The JSON has building blocks of types aggregate, entity, value_object, domain_event, domain_service, application_service, and repository. Project at /Users/me/projects/ecommerce/", "should_trigger": true}, + {"query": "I updated the design document with new behaviours on the Order aggregate and a new ShippingAddress value object. Can you sync the Java code to match what's in the updated designdoc json? Some classes need modification, others are new.", "should_trigger": true}, + {"query": "zaimplementuj design doc json do mojego projektu java — plik jest w contracts/design-doc.json, projekt w src/. JSON zawiera boundedContexts, buildingBlocks, useCases i rules", "should_trigger": true}, + {"query": "our architect produced a DesignDoc JSON with the full domain model for the HR system — 3 bounded contexts, 15 building blocks, use cases with scenarios. I need all of this turned into Java classes following our existing project conventions at hr-service/src/main/java/", "should_trigger": true}, + {"query": "the team designed a new Claims module and exported it as a DesignDoc JSON. It references existing types from the Policy module (PolicyId, Premium). Need to implement the Java code, making sure to import existing types instead of recreating them", "should_trigger": true}, + {"query": "Can you write a REST API in Spring Boot for managing users? I need endpoints for CRUD operations with validation and proper error handling", "should_trigger": false}, + {"query": "I need to refactor this Java service class — it's 500 lines long and does too many things. Can you help me split it into smaller classes following single responsibility principle?", "should_trigger": false}, + {"query": "help me write unit tests for my Order aggregate using JUnit 5 and Mockito. The aggregate is at src/main/java/com/shop/orders/Order.java", "should_trigger": false}, + {"query": "I want to create a design document for our new feature. We need to describe the domain model, bounded contexts, and building blocks. Can you help me write the design doc?", "should_trigger": false}, + {"query": "can you review this java code and tell me if it follows DDD patterns correctly? I'm not sure if my aggregate boundaries are right", "should_trigger": false}, + {"query": "I need to set up a new Spring Boot project with Java 21, Maven, and the usual dependencies (web, jpa, lombok). Can you create the project structure?", "should_trigger": false}, + {"query": "convert this Python dataclass model to Java records — I have a User class with name, email, and created_at fields", "should_trigger": false}, + {"query": "I have a JSON schema file that describes our API request/response models. Can you generate Java DTOs from it using Jackson annotations?", "should_trigger": false}, + {"query": "we're migrating from Java 11 to Java 21 — can you update our value objects from final classes to records and our domain events to use sealed interfaces?", "should_trigger": false}, + {"query": "implement the database migration scripts for our new Order tables based on the domain model in src/main/java/com/shop/orders/", "should_trigger": false} +] diff --git a/src/agent_extensions/skills/implement_design_doc_java/implement_design_doc_java.skill b/src/agent_extensions/skills/implement_design_doc_java/implement_design_doc_java.skill new file mode 100644 index 0000000..baeced7 Binary files /dev/null and b/src/agent_extensions/skills/implement_design_doc_java/implement_design_doc_java.skill differ diff --git a/src/agent_extensions/skills/implement_design_doc_java/references/designdoc_mapping.md b/src/agent_extensions/skills/implement_design_doc_java/references/designdoc_mapping.md new file mode 100644 index 0000000..8ddd59e --- /dev/null +++ b/src/agent_extensions/skills/implement_design_doc_java/references/designdoc_mapping.md @@ -0,0 +1,197 @@ +# DesignDoc JSON to Java Mapping Rules + +This document defines how each element in a DesignDoc JSON maps to Java code. These rules are non-negotiable — they define correctness. Style (how the code looks) is determined by the target project's conventions. + +## Package Structure + +The bounded context / module hierarchy determines the Java package structure: + +``` +BoundedContext "Subscription" + └── DomainModule "Company" + └── BuildingBlock "CompanySubscription" (aggregate) +``` + +Maps to a package like: +``` +{base.package}.subscription.company +``` + +The exact mapping depends on the project's existing conventions. If the project uses layered sub-packages (e.g., `domain/`, `application/`), follow that. If the project keeps everything flat within a module package, follow that. + +Building blocks not assigned to any module but assigned to a bounded context go directly under the context's package. + +## Handling "Already Exists" Building Blocks + +Some building blocks in the DesignDoc have descriptions like "Already exists in the project" or similar. For these: + +1. **Search the project** for a class with that exact name using Glob or Grep +2. **Import it** from its existing location — do NOT create a new file +3. **Reference its existing API** (method names, field names) rather than assuming the DesignDoc properties are its interface +4. If the class genuinely does not exist in the project despite the description, create it in the shared/common package following the project's conventions — not in the new module's package + +This prevents duplicate classes across packages, which breaks type compatibility. + +## BuildingBlock Type → Java Class + +### `aggregate` + +The aggregate root class. This is the entry point for all operations on the aggregate. + +- **Properties** → private fields (or records fields if project uses records for aggregates, which is uncommon) +- **Behaviours** → public methods on the aggregate root + - `input` references → method parameters (resolve to the referenced building block's Java type) + - `output` references → return type or emitted events (resolve to the referenced building block's Java type) + - `rules` references → business logic enforced inside the method body +- **Description** → parse for mentions of child entities/value objects that this aggregate contains + +Aggregates own their internal entities. If the description mentions containment (e.g., "contains OrderItem entities"), those entities should be package-private or inner classes accessible only through the aggregate. + +### `entity` + +An entity within an aggregate or standing alone. + +- **Properties** → private fields, one of which is the identity field +- **Behaviours** → methods that mutate state or query state +- Entities have identity — ensure equals/hashCode is based on the identity field +- If the entity belongs to an aggregate (mentioned in aggregate's description), it should have restricted visibility (package-private or inner class) + +### `value_object` + +Immutable type representing a domain concept without identity. + +- **Properties** → final fields (or record components) +- **Behaviours** → methods (typically returning new instances for transformations) +- Must be immutable — no setters, all fields final +- Validation in constructor (or static factory) based on referenced rules +- Equality by all fields (structural equality) + +### `domain_event` + +An immutable record of something that happened. + +- **Properties** → final fields capturing the event data +- Must be immutable +- Name should be past-tense (the JSON `name` field defines this) +- Typically includes a timestamp and relevant aggregate identifiers + +**Critical: match the project's event pattern exactly.** Projects use different mechanisms: +- **Sealed interface with inner records** (modern Java): `sealed interface Event permits Deposited, Withdrawn {}` with `record Deposited(...) implements Event {}` inside the aggregate class +- **Marker interface with separate classes**: `interface DomainEvent {}` + `@Value public class OrderCreated implements DomainEvent {}` +- **Plain classes**: separate file per event, manual equals/hashCode + +Look at how the **aggregate** in the project stores and emits events — e.g., `List pendingEvents` vs `List pendingEvents` vs `List`. Use the same type. If the project uses a sealed interface scoped to the aggregate, your new aggregate must define its own sealed interface for its events. + +### `domain_command` + +A request to perform an action. + +- **Properties** → fields capturing the command data +- Should be immutable +- Name should be imperative (the JSON `name` field defines this) + +### `domain_query` + +A request for information. + +- **Properties** → fields capturing the query parameters +- Should be immutable + +### `domain_service` + +Stateless domain logic that doesn't belong to a single aggregate. + +- **Behaviours** → methods coordinating multiple domain objects +- No mutable state — all dependencies injected +- `input`/`output` on behaviours determine method signatures + +### `application_service` + +Orchestrates use cases by coordinating domain objects, repositories, and services. + +- **Behaviours** → use case methods (typically one per service, or one per use case) +- Depends on repositories and domain services +- Handles transaction boundaries +- The `useCases` section of the DesignDoc provides additional context for what application services should orchestrate + +### `repository` + +Interface for persisting and retrieving aggregates. + +- **Behaviours** → interface methods (save, find, delete, etc.) +- `input`/`output` reference the aggregate type +- Implementation is OUT OF SCOPE — only generate the interface +- Follow project's repository naming and return type conventions (Optional, nullable, etc.) + +**Critical: check how the project structures repository interfaces.** Common patterns: +- **Separate interface file** in the domain package (most common) +- **Nested interface inside the aggregate** (e.g., `Order.Repository`) — some projects nest the repository interface inside the aggregate root class to express the tight coupling + +If the project uses nested repository interfaces, your new aggregate must also define its repository as a nested interface. + +### `factory` + +Encapsulates complex creation logic. + +- **Behaviours** → factory methods +- `output` references → the type being created +- `input` references → the data needed for creation +- Can be a static method on the aggregate, a separate class, or an abstract factory — match project conventions + +### `external_integration` + +Interface for external system communication. + +- Generate as an interface (port) in the domain layer +- Implementation is OUT OF SCOPE + +## Rules Mapping + +Rules referenced by behaviours map to business logic within the method: + +| Rule Type | Java Implementation | +|---|---| +| `Consistency` | Invariant check — validate state before/after mutation, throw or return failure if violated | +| `Structure` | Structural constraint — enforce in constructor or factory (field constraints, required relationships) | +| `Computation` | Calculation logic — implement the formula described in the rule's `description` | +| `State change` | State transition guard — check preconditions before allowing state change | + +The rule's `description` field contains the actual business rule in natural language. Translate it to code. + +## UseCases Mapping + +Use cases in the DesignDoc provide higher-level orchestration context: + +- `type: Command` → a use case that changes state. Map to an application service method. +- `type: Event` → a use case triggered by an event. Map to an event handler. +- `type: Query` → a read operation. Map to a query service or read model. + +The `usedBuildingBlocks` field tells you which building blocks the use case orchestrates. +The `input`/`output` fields tell you the command/query/event types and the response types. +The `scenarios` (Given/When/Then) describe the expected behavior — useful for understanding the method's logic. +The `rules` field lists business rules that must be enforced in this use case. + +## Behaviour → Method Mapping Details + +For each behaviour on a building block: + +```json +{ + "name": "enroll", + "description": "Enrolls a subscriber if subscription is active and has capacity", + "input": ["bb-subscriber-id"], + "output": ["bb-subscriber-enrolled-event"], + "rules": ["rule-active-subscription", "rule-capacity-check"] +} +``` + +Maps to: + +```java +// Method name from behaviour.name +// Parameter type from resolving bb-subscriber-id → SubscriberId +// Return type from resolving bb-subscriber-enrolled-event → SubscriberEnrolled (or Result containing it) +// Body enforces rule-active-subscription and rule-capacity-check +``` + +The exact method signature (return type wrapping, exception style) depends on the project's detected conventions.