From 38a369840fc501db0657bf31307f18f16822b017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Li=C5=BCewski?= Date: Sat, 13 Jun 2026 21:48:30 +0200 Subject: [PATCH 1/3] refactor process - IV stage --- .../chain/MarkdownInstructionProvider.java | 14 ++-- aleatoricism-spring-boot-sample-app/README.md | 18 +++-- .../docker-compose.yml | 9 --- aleatoricism-spring-boot-sample-app/pom.xml | 26 ++++---- .../scitia/app/sprint/api/IssueDto.java | 12 ---- .../scitia/app/sprint/api/SprintDto.java | 6 -- .../app/sprint/api/SprintPlanningRequest.java | 7 -- .../scitia/app/sprint/domain/Issue.java | 35 ---------- .../scitia/app/sprint/domain/Sprint.java | 36 ---------- .../scitia/app/sprint/flow/SprintFlows.java | 25 +++---- .../flow/plan/AddIssuesToSprintWaypoint.java | 25 ------- .../flow/plan/CreateSprintEntityWaypoint.java | 20 ------ .../flow/plan/PersistSprintWaypoint.java | 22 ------- ...ValidateSprintPlanningRequestWaypoint.java | 57 ---------------- .../infrastructure/SprintRepository.java | 8 --- .../scitia/app/sprint/mapper/IssueMapper.java | 19 ------ .../app/sprint/mapper/SprintMapper.java | 26 -------- .../AleatoricismChainController.java | 2 +- .../core/AleatoricCopilotGateway.java | 37 ++++++----- .../agile/AgileSprintPlanningToolHandler.java | 6 +- .../src/main/resources/aleatoricism.md | 66 ------------------- .../src/main/resources/application.yaml | 10 +++ .../AgileSprintPlanningToolHandlerTest.java | 2 +- 23 files changed, 77 insertions(+), 411 deletions(-) delete mode 100644 aleatoricism-spring-boot-sample-app/docker-compose.yml delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/IssueDto.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintDto.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintPlanningRequest.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Issue.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Sprint.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/AddIssuesToSprintWaypoint.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/CreateSprintEntityWaypoint.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/PersistSprintWaypoint.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/ValidateSprintPlanningRequestWaypoint.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/infrastructure/SprintRepository.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/IssueMapper.java delete mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/SprintMapper.java diff --git a/aleatoricism-chain/src/main/java/io/github/scitia/aleatoricism/chain/MarkdownInstructionProvider.java b/aleatoricism-chain/src/main/java/io/github/scitia/aleatoricism/chain/MarkdownInstructionProvider.java index 16ae099..8949f72 100644 --- a/aleatoricism-chain/src/main/java/io/github/scitia/aleatoricism/chain/MarkdownInstructionProvider.java +++ b/aleatoricism-chain/src/main/java/io/github/scitia/aleatoricism/chain/MarkdownInstructionProvider.java @@ -47,21 +47,21 @@ public String getInstructions() { private String getDefaultInstructions() { return """ # Aleatoric HTTP Router Instructions - - This is the default instruction set. - To customize, create 'aleatoric-instructions.md' in your project's resources folder. - + \s + This is the default instruction set.\s + To customize, create 'aleatoricism.md' in your project's resources folder. + \s ## HTTP Method Handling - + \s The router will automatically handle: - GET: Retrieve resources - POST: Create resources - PUT: Replace resources - PATCH: Update resources - DELETE: Remove resources - + \s Each request will be processed by the AI model which will decide the appropriate business operation. - """; + \s"""; } } diff --git a/aleatoricism-spring-boot-sample-app/README.md b/aleatoricism-spring-boot-sample-app/README.md index 5c36b19..b42dd5a 100644 --- a/aleatoricism-spring-boot-sample-app/README.md +++ b/aleatoricism-spring-boot-sample-app/README.md @@ -10,11 +10,17 @@ This sample app includes domain flows for quotes and agile scrum workflows. The ```json { - "name": "Sprint 12", - "goal": "Ship planning improvements", - "startDate": "2026-06-01", - "endDate": "2026-06-14", - "capacity": 30, - "team": "alpha" + "name": "Name", + "goal": "Goal", + "issues": [ + { + "name": "Issue 1", + "description": "Description 1", + "reporter": "Reporter 1", + "estimation": 5, + "acceptanceCriteria": ["Criterion 1"] + } + ] } +``` diff --git a/aleatoricism-spring-boot-sample-app/docker-compose.yml b/aleatoricism-spring-boot-sample-app/docker-compose.yml deleted file mode 100644 index 29c7c93..0000000 --- a/aleatoricism-spring-boot-sample-app/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - database: - image: 'postgres:15.2' - ports: - - '5432:5432' - environment: - - 'POSTGRES_USER=appuser' - - 'POSTGRES_DB=aleatoricismdb' - - 'POSTGRES_PASSWORD=topsecret' \ No newline at end of file diff --git a/aleatoricism-spring-boot-sample-app/pom.xml b/aleatoricism-spring-boot-sample-app/pom.xml index f1a142c..6f25bc8 100644 --- a/aleatoricism-spring-boot-sample-app/pom.xml +++ b/aleatoricism-spring-boot-sample-app/pom.xml @@ -16,6 +16,9 @@ 2.2.224 3.10.0.2594 0.2.2-java.1 + 1.18.46 + 1.6.3 + 3.13.0 @@ -44,24 +47,23 @@ org.projectlombok lombok - 1.18.46 - - - org.springframework.boot - spring-boot-docker-compose - true - runtime + ${lombok.version} com.h2database h2 - 2.2.224 + ${h2.version} test org.mapstruct mapstruct - 1.6.3 + ${mapstruct.version} + + + org.postgresql + postgresql + runtime @@ -79,18 +81,18 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + ${maven.compiler.plugin.version} org.mapstruct mapstruct-processor - 1.6.3 + ${mapstruct.version} org.projectlombok lombok - 1.18.46 + ${lombok.version} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/IssueDto.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/IssueDto.java deleted file mode 100644 index 3c5fab6..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/IssueDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.scitia.app.sprint.api; - -import java.util.List; - -public record IssueDto( - String name, - String description, - String reporter, - Integer estimation, - List acceptanceCriteria -) { -} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintDto.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintDto.java deleted file mode 100644 index bf15437..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintDto.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.scitia.app.sprint.api; - -import java.util.List; - -public record SprintDto(Long id, String name, String goal, List issues) { -} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintPlanningRequest.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintPlanningRequest.java deleted file mode 100644 index 23eec31..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/api/SprintPlanningRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.scitia.app.sprint.api; - -import java.util.List; - -public record SprintPlanningRequest(String name, String goal, List issues) { -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Issue.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Issue.java deleted file mode 100644 index a4e36c3..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Issue.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.scitia.app.sprint.domain; - -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Setter -@Getter -@Entity -public class Issue { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String name; - private String description; - private String reporter; - private Integer estimation; - - @ElementCollection(fetch = FetchType.EAGER) - private List acceptanceCriteria; - - @ManyToOne - private Sprint sprint; -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Sprint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Sprint.java deleted file mode 100644 index 058d2b8..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/Sprint.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.scitia.app.sprint.domain; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@Getter -@Setter -@Entity -public class Sprint { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String name; - private String goal; - - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "sprint") - private List issues = new ArrayList<>(); - - public void addIssue(Issue issue) { - issues.add(issue); - issue.setSprint(this); - } -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java index 911806b..a8a0501 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java @@ -2,15 +2,14 @@ import io.github.scitia.aleatoricism.flows.engine.BusinessFlowBuilder; import io.github.scitia.aleatoricism.flows.engine.Flow; -import io.github.scitia.app.sprint.api.SprintPlanningRequest; -import io.github.scitia.app.sprint.domain.Sprint; -import io.github.scitia.app.sprint.flow.plan.AddIssuesToSprintWaypoint; -import io.github.scitia.app.sprint.flow.plan.CreateSprintEntityWaypoint; -import io.github.scitia.app.sprint.flow.plan.PersistSprintWaypoint; -import io.github.scitia.app.sprint.flow.plan.ValidateSprintPlanningRequestWaypoint; -import lombok.Getter; +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; +import io.github.scitia.app.sprint.domain.sprint.Sprint; +import io.github.scitia.app.sprint.flow.plan.waypoint.AddIssuesToSprintWaypoint; +import io.github.scitia.app.sprint.flow.plan.waypoint.CreateSprintEntityWaypoint; +import io.github.scitia.app.sprint.flow.plan.waypoint.PersistSprintWaypoint; +import io.github.scitia.app.sprint.flow.plan.waypoint.ValidateSprintPlanningRequestWaypoint; +import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; import lombok.RequiredArgsConstructor; -import lombok.Setter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -20,16 +19,10 @@ public class SprintFlows { private final PersistSprintWaypoint persistSprintWaypoint; - @Setter - @Getter - public static class ExampleStore { - private SprintPlanningRequest sprintPlanningRequest; - } - @Bean(name = "sprint-planning-flow") - public Flow sprintPlanningFlow() { + public Flow sprintPlanningFlow() { return BusinessFlowBuilder.define() - .start(new ValidateSprintPlanningRequestWaypoint(), ExampleStore::new) + .start(new ValidateSprintPlanningRequestWaypoint(), SprintPlanningStore::new) .then(new CreateSprintEntityWaypoint()) .then(new AddIssuesToSprintWaypoint()) .then(persistSprintWaypoint) diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/AddIssuesToSprintWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/AddIssuesToSprintWaypoint.java deleted file mode 100644 index 315a48f..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/AddIssuesToSprintWaypoint.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.scitia.app.sprint.flow.plan; - -import io.github.scitia.aleatoricism.flows.api.Waypoint; -import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; -import io.github.scitia.app.sprint.domain.Issue; -import io.github.scitia.app.sprint.domain.Sprint; -import io.github.scitia.app.sprint.flow.SprintFlows; - -public class AddIssuesToSprintWaypoint implements Waypoint { - - @Override - public Sprint handle(Sprint sprint, ExecutionContext context) { - context.getStore().getSprintPlanningRequest().issues().forEach(issueDto -> { - Issue issue = new Issue(); - issue.setName(issueDto.name()); - issue.setDescription(issueDto.description()); - issue.setReporter(issueDto.reporter()); - issue.setEstimation(issueDto.estimation()); - issue.setAcceptanceCriteria(issueDto.acceptanceCriteria()); - sprint.addIssue(issue); - }); - return sprint; - } -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/CreateSprintEntityWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/CreateSprintEntityWaypoint.java deleted file mode 100644 index 55aa2e9..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/CreateSprintEntityWaypoint.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.scitia.app.sprint.flow.plan; - -import io.github.scitia.aleatoricism.flows.api.Waypoint; -import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; -import io.github.scitia.app.sprint.api.SprintPlanningRequest; -import io.github.scitia.app.sprint.domain.Sprint; -import io.github.scitia.app.sprint.flow.SprintFlows; - -public class CreateSprintEntityWaypoint implements Waypoint { - - @Override - public Sprint handle(SprintPlanningRequest input, ExecutionContext context) throws Exception { - - Sprint sprint = new Sprint(); - sprint.setName(input.name()); - sprint.setGoal(input.goal()); - return sprint; - } -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/PersistSprintWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/PersistSprintWaypoint.java deleted file mode 100644 index ecc6260..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/PersistSprintWaypoint.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.scitia.app.sprint.flow.plan; - -import io.github.scitia.aleatoricism.flows.api.Waypoint; -import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; -import io.github.scitia.app.sprint.domain.Sprint; -import io.github.scitia.app.sprint.flow.SprintFlows; -import io.github.scitia.app.sprint.infrastructure.SprintRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class PersistSprintWaypoint implements Waypoint { - - private final SprintRepository sprintRepository; - - @Override - public Sprint handle(Sprint sprint, ExecutionContext context) { - return sprintRepository.save(sprint); - } -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/ValidateSprintPlanningRequestWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/ValidateSprintPlanningRequestWaypoint.java deleted file mode 100644 index 588820b..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/ValidateSprintPlanningRequestWaypoint.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.scitia.app.sprint.flow.plan; - -import io.github.scitia.aleatoricism.flows.api.Waypoint; -import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; -import io.github.scitia.app.sprint.api.IssueDto; -import io.github.scitia.app.sprint.api.SprintPlanningRequest; -import io.github.scitia.app.sprint.flow.SprintFlows; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -public class ValidateSprintPlanningRequestWaypoint implements Waypoint { - - @Override - public SprintPlanningRequest handle(SprintPlanningRequest input, ExecutionContext context) throws Exception { - if (input == null) { - throw new IllegalArgumentException("Sprint planning request cannot be null."); - } - if (!StringUtils.hasText(input.name())) { - throw new IllegalArgumentException("Sprint name cannot be empty."); - } - if (!StringUtils.hasText(input.goal())) { - throw new IllegalArgumentException("Sprint goal cannot be empty."); - } - if (CollectionUtils.isEmpty(input.issues())) { - throw new IllegalArgumentException("Sprint must have at least one issue."); - } - for (IssueDto issue : input.issues()) { - validateIssue(issue); - } - - context.getStore().setSprintPlanningRequest(input); - - return input; - } - - private void validateIssue(IssueDto issue) { - if (issue == null) { - throw new IllegalArgumentException("Issue cannot be null."); - } - if (!StringUtils.hasText(issue.name())) { - throw new IllegalArgumentException("Issue name cannot be empty."); - } - if (!StringUtils.hasText(issue.description())) { - throw new IllegalArgumentException("Issue description cannot be empty."); - } - if (!StringUtils.hasText(issue.reporter())) { - throw new IllegalArgumentException("Issue reporter cannot be empty."); - } - if (issue.estimation() == null || issue.estimation() <= 0) { - throw new IllegalArgumentException("Issue estimation must be a positive number."); - } - if (CollectionUtils.isEmpty(issue.acceptanceCriteria())) { - throw new IllegalArgumentException("Issue must have at least one acceptance criterion."); - } - } -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/infrastructure/SprintRepository.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/infrastructure/SprintRepository.java deleted file mode 100644 index 8ad2f76..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/infrastructure/SprintRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.scitia.app.sprint.infrastructure; - -import io.github.scitia.app.sprint.domain.Sprint; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SprintRepository extends JpaRepository { -} - diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/IssueMapper.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/IssueMapper.java deleted file mode 100644 index 7ee3286..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/IssueMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.scitia.app.sprint.mapper; - -import io.github.scitia.app.sprint.api.IssueDto; -import io.github.scitia.app.sprint.domain.Issue; -import org.springframework.stereotype.Component; - -@Component -public class IssueMapper { - - public IssueDto mapToDto(Issue issue) { - return new IssueDto( - issue.getName(), - issue.getDescription(), - issue.getReporter(), - issue.getEstimation(), - issue.getAcceptanceCriteria() - ); - } -} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/SprintMapper.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/SprintMapper.java deleted file mode 100644 index b7e9aaf..0000000 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/mapper/SprintMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.scitia.app.sprint.mapper; - -import io.github.scitia.app.sprint.api.IssueDto; -import io.github.scitia.app.sprint.api.SprintDto; -import io.github.scitia.app.sprint.domain.Sprint; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.util.List; - -@Component -@RequiredArgsConstructor -public class SprintMapper { - - private final IssueMapper issueMapper; - - public SprintDto mapToDto(Sprint sprint) { - - List issues = sprint.getIssues() - .stream() - .map(issueMapper::mapToDto) - .toList(); - - return new SprintDto(sprint.getId(), sprint.getName(), sprint.getGoal(), issues); - } -} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/controller/AleatoricismChainController.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/controller/AleatoricismChainController.java index 0c95251..a01d873 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/controller/AleatoricismChainController.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/controller/AleatoricismChainController.java @@ -17,7 +17,7 @@ import java.util.Map; @RestController -@RequestMapping(path = "/api/aleachain", produces = MediaType.APPLICATION_JSON_VALUE) +@RequestMapping(path = "/api/chain", produces = MediaType.APPLICATION_JSON_VALUE) public class AleatoricismChainController { private final AleatoricCopilotGateway gateway; diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java index 4de5488..201c058 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java @@ -79,7 +79,6 @@ public AleatoricCommandResponse execute(@Valid AleatoricCommandRequest request) var done = new CompletableFuture(); session.on(SessionIdleEvent.class, _ -> done.complete(null)); - session.send(new MessageOptions().setPrompt(buildAgentPrompt(request))).get(); done.get(); } catch (InterruptedException exception) { @@ -123,25 +122,29 @@ private String buildAgentPrompt(AleatoricCommandRequest request) { metadataJson = String.valueOf(request.metadata()); } + return """ - You are an execution planner for business tools. - Choose tool(s) from the catalog and invoke them with proper JSON arguments. - You may call multiple tools if needed, but only if each call adds business value. - If no tool can safely satisfy intent, fail with a clear exception. - - Tool catalog: - %s - - Intent: %s - Instructions: %s - Payload: %s - Metadata: %s - """.formatted( + You are an AI agent that MUST use one of the available tools to handle the request. + + AVAILABLE TOOLS: + %s + + USER INTENT: + %s + + INPUT DATA: + %s + + IMPORTANT: + - You MUST call a tool + - Do NOT answer directly + - Select the most appropriate tool + - Return ONLY tool execution + + """.formatted( toolsSummary, safeText(request.intent()), - safeText(request.instructions()), - payloadJson, - metadataJson + payloadJson ); } diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java index f29d560..c17c6e9 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java @@ -4,10 +4,10 @@ import com.github.copilot.sdk.json.ToolHandler; import com.github.copilot.sdk.json.ToolInvocation; import io.github.scitia.aleatoricism.flows.engine.FlowEngine; -import io.github.scitia.app.sprint.api.SprintPlanningRequest; -import io.github.scitia.app.sprint.domain.Sprint; +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; +import io.github.scitia.app.sprint.domain.sprint.Sprint; import io.github.scitia.app.sprint.flow.SprintFlows; -import io.github.scitia.app.sprint.mapper.SprintMapper; +import io.github.scitia.app.sprint.domain.sprint.SprintMapper; import io.github.scitia.config.aleatoricism.tools.AgenticBusinessTool; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; diff --git a/aleatoricism-spring-boot-sample-app/src/main/resources/aleatoricism.md b/aleatoricism-spring-boot-sample-app/src/main/resources/aleatoricism.md index 828d522..139597f 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/resources/aleatoricism.md +++ b/aleatoricism-spring-boot-sample-app/src/main/resources/aleatoricism.md @@ -1,68 +1,2 @@ -# Scrum Processes - Aleatoricism Flow Router -## Overview - -This application implements three core Scrum processes as agentic workflows. The HTTP router will analyze requests -and route them to the appropriate Scrum process based on the intent and path. - -## Scrum Processes and Endpoints - -### 1. Sprint Planning -- **Path**: `/scrum/planning` or `/scrum/sprint/{sprintId}/planning` -- **Method**: POST -- **Purpose**: Plan a sprint by selecting backlog items, estimating workload, and validating against team capacity -- **Tool**: `scrum_sprint_planning` -- **Practical aspects**: - - Validates story points against team capacity - - Detects capacity overflow situations - - Commits specific backlog items to sprint - - Ensures realistic sprint goals - - Emits audit trail for commitment tracking - -## Request Format - -All Scrum process requests should: -- Use **POST** method -- Include a **JSON body** with process-specific parameters -- Follow the input schema defined by each tool handler - -### Example Request Paths: -``` -POST /scrum/planning -POST /scrum/sprint/550e8400-e29b-41d4-a716-446655440000/planning -``` - -## Response Format - -All endpoints return a JSON response with: -- ID of the created/processed entity -- Status of the operation -- Relevant metadata for the Scrum process -- Audit trail information - -## Tool Availability - -Available Scrum tools: -- `scrum_sprint_planning` - Plan sprint backlog and validate capacity - -## Error Handling - -If the AI model cannot safely execute a request: -- Return a clear error message describing the issue -- Do not attempt to execute if the intent is unclear -- Always validate that required parameters are present -- Provide helpful feedback on missing or invalid data - -## Workflow Stages - -Each Scrum process follows this pattern: -1. **Normalize** - Sanitize input, apply defaults, handle missing values -2. **Build** - Create domain object with calculated values -3. **Audit** - Emit event for process tracking and monitoring - ---- - -**Note**: Modify this file to customize Scrum process handling. Restart the application for changes to take effect. - -For benchmarking and performance testing, all three processes are instrumented with audit trails. diff --git a/aleatoricism-spring-boot-sample-app/src/main/resources/application.yaml b/aleatoricism-spring-boot-sample-app/src/main/resources/application.yaml index 9b779d5..76f8d04 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/resources/application.yaml +++ b/aleatoricism-spring-boot-sample-app/src/main/resources/application.yaml @@ -6,6 +6,16 @@ server: spring: application: name: aleatoricism-app + datasource: + url: jdbc:postgresql://localhost:5432/aleatoricismdb + username: appuser + password: topsecret + jpa: + hibernate: + ddl-auto: update + show-sql: true + open-in-view: false + generate-ddl: true aleatoricism: auto-router: diff --git a/aleatoricism-spring-boot-sample-app/src/test/java/io/github/scitia/aleachain/tools/AgileSprintPlanningToolHandlerTest.java b/aleatoricism-spring-boot-sample-app/src/test/java/io/github/scitia/aleachain/tools/AgileSprintPlanningToolHandlerTest.java index 6764dad..7bc08ea 100644 --- a/aleatoricism-spring-boot-sample-app/src/test/java/io/github/scitia/aleachain/tools/AgileSprintPlanningToolHandlerTest.java +++ b/aleatoricism-spring-boot-sample-app/src/test/java/io/github/scitia/aleachain/tools/AgileSprintPlanningToolHandlerTest.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.copilot.sdk.json.ToolInvocation; -import io.github.scitia.app.sprint.api.SprintDto; +import io.github.scitia.app.sprint.domain.sprint.SprintDto; import io.github.scitia.config.aleatoricism.tools.app.agile.AgileSprintPlanningToolHandler; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; From 7441ac85fed7f316ee1d5e988f477af2e962d04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Li=C5=BCewski?= Date: Sun, 14 Jun 2026 00:01:04 +0200 Subject: [PATCH 2/3] refactor process - V stage --- .../scitia/app/sprint/domain/issue/Issue.java | 36 ++++++++++++ .../app/sprint/domain/issue/IssueDto.java | 12 ++++ .../app/sprint/domain/issue/IssueMapper.java | 17 ++++++ .../sprint/domain/issue/IssueRepository.java | 6 ++ .../app/sprint/domain/sprint/Sprint.java | 37 ++++++++++++ .../app/sprint/domain/sprint/SprintDto.java | 8 +++ .../sprint/domain/sprint/SprintMapper.java | 25 ++++++++ .../domain/sprint/SprintRepository.java | 7 +++ .../flow/api/SprintPlanningRequest.java | 9 +++ .../flow/plan/store/SprintPlanningStore.java | 13 +++++ .../waypoint/AddIssuesToSprintWaypoint.java | 25 ++++++++ .../waypoint/CreateSprintEntityWaypoint.java | 19 +++++++ .../plan/waypoint/PersistSprintWaypoint.java | 22 +++++++ ...ValidateSprintPlanningRequestWaypoint.java | 57 +++++++++++++++++++ .../core/AleatoricCopilotGateway.java | 37 ++++++------ .../agile/AgileSprintPlanningToolHandler.java | 8 +-- 16 files changed, 313 insertions(+), 25 deletions(-) create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/Issue.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueMapper.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueRepository.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/Sprint.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintDto.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintMapper.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintRepository.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/store/SprintPlanningStore.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/AddIssuesToSprintWaypoint.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/CreateSprintEntityWaypoint.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/PersistSprintWaypoint.java create mode 100644 aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/Issue.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/Issue.java new file mode 100644 index 0000000..0320f6b --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/Issue.java @@ -0,0 +1,36 @@ +package io.github.scitia.app.sprint.domain.issue; + +import io.github.scitia.app.sprint.domain.sprint.Sprint; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@Entity +public class Issue { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String description; + private String reporter; + private Integer estimation; + + @ElementCollection(fetch = FetchType.EAGER) + private List acceptanceCriteria; + + @ManyToOne + private Sprint sprint; +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java new file mode 100644 index 0000000..e3a0752 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java @@ -0,0 +1,12 @@ +package io.github.scitia.app.sprint.domain.issue; + +import java.util.List; + +public record IssueDto( + String name, + String description, + String reporter, + Integer estimation, + List acceptanceCriteria +) { +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueMapper.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueMapper.java new file mode 100644 index 0000000..fa2976c --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueMapper.java @@ -0,0 +1,17 @@ +package io.github.scitia.app.sprint.domain.issue; + +import org.springframework.stereotype.Component; + +@Component +public class IssueMapper { + + public IssueDto mapToDto(Issue issue) { + return new IssueDto( + issue.getName(), + issue.getDescription(), + issue.getReporter(), + issue.getEstimation(), + issue.getAcceptanceCriteria() + ); + } +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueRepository.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueRepository.java new file mode 100644 index 0000000..321db98 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueRepository.java @@ -0,0 +1,6 @@ +package io.github.scitia.app.sprint.domain.issue; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface IssueRepository extends JpaRepository { +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/Sprint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/Sprint.java new file mode 100644 index 0000000..e1846fe --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/Sprint.java @@ -0,0 +1,37 @@ +package io.github.scitia.app.sprint.domain.sprint; + +import io.github.scitia.app.sprint.domain.issue.Issue; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Entity +public class Sprint { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String goal; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "sprint") + private List issues = new ArrayList<>(); + + public void addIssue(Issue issue) { + issues.add(issue); + issue.setSprint(this); + } +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintDto.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintDto.java new file mode 100644 index 0000000..c84c667 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintDto.java @@ -0,0 +1,8 @@ +package io.github.scitia.app.sprint.domain.sprint; + +import io.github.scitia.app.sprint.domain.issue.IssueDto; + +import java.util.List; + +public record SprintDto(Long id, String name, String goal, List issues) { +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintMapper.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintMapper.java new file mode 100644 index 0000000..da4914c --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintMapper.java @@ -0,0 +1,25 @@ +package io.github.scitia.app.sprint.domain.sprint; + +import io.github.scitia.app.sprint.domain.issue.IssueDto; +import io.github.scitia.app.sprint.domain.issue.IssueMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class SprintMapper { + + private final IssueMapper issueMapper; + + public SprintDto mapToDto(Sprint sprint) { + + List issues = sprint.getIssues() + .stream() + .map(issueMapper::mapToDto) + .toList(); + + return new SprintDto(sprint.getId(), sprint.getName(), sprint.getGoal(), issues); + } +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintRepository.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintRepository.java new file mode 100644 index 0000000..eee4723 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/sprint/SprintRepository.java @@ -0,0 +1,7 @@ +package io.github.scitia.app.sprint.domain.sprint; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SprintRepository extends JpaRepository { +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java new file mode 100644 index 0000000..dcbcacd --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java @@ -0,0 +1,9 @@ +package io.github.scitia.app.sprint.flow.api; + +import io.github.scitia.app.sprint.domain.issue.IssueDto; + +import java.util.List; + +public record SprintPlanningRequest(String name, String goal, List issues) { +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/store/SprintPlanningStore.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/store/SprintPlanningStore.java new file mode 100644 index 0000000..94ca33d --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/store/SprintPlanningStore.java @@ -0,0 +1,13 @@ +package io.github.scitia.app.sprint.flow.plan.store; + +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class SprintPlanningStore { + private SprintPlanningRequest sprintPlanningRequest; +} diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/AddIssuesToSprintWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/AddIssuesToSprintWaypoint.java new file mode 100644 index 0000000..8f7bf52 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/AddIssuesToSprintWaypoint.java @@ -0,0 +1,25 @@ +package io.github.scitia.app.sprint.flow.plan.waypoint; + +import io.github.scitia.aleatoricism.flows.api.Waypoint; +import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; +import io.github.scitia.app.sprint.domain.issue.Issue; +import io.github.scitia.app.sprint.domain.sprint.Sprint; +import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; + +public class AddIssuesToSprintWaypoint implements Waypoint { + + @Override + public Sprint handle(Sprint sprint, ExecutionContext context) { + context.getStore().getSprintPlanningRequest().issues().forEach(issueDto -> { + Issue issue = new Issue(); + issue.setName(issueDto.name()); + issue.setDescription(issueDto.description()); + issue.setReporter(issueDto.reporter()); + issue.setEstimation(issueDto.estimation()); + issue.setAcceptanceCriteria(issueDto.acceptanceCriteria()); + sprint.addIssue(issue); + }); + return sprint; + } +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/CreateSprintEntityWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/CreateSprintEntityWaypoint.java new file mode 100644 index 0000000..24dc672 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/CreateSprintEntityWaypoint.java @@ -0,0 +1,19 @@ +package io.github.scitia.app.sprint.flow.plan.waypoint; + +import io.github.scitia.aleatoricism.flows.api.Waypoint; +import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; +import io.github.scitia.app.sprint.domain.sprint.Sprint; +import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; + +public class CreateSprintEntityWaypoint implements Waypoint { + + @Override + public Sprint handle(SprintPlanningRequest input, ExecutionContext context) { + Sprint sprint = new Sprint(); + sprint.setName(input.name()); + sprint.setGoal(input.goal()); + return sprint; + } +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/PersistSprintWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/PersistSprintWaypoint.java new file mode 100644 index 0000000..f57abf4 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/PersistSprintWaypoint.java @@ -0,0 +1,22 @@ +package io.github.scitia.app.sprint.flow.plan.waypoint; + +import io.github.scitia.aleatoricism.flows.api.Waypoint; +import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; +import io.github.scitia.app.sprint.domain.sprint.Sprint; +import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; +import io.github.scitia.app.sprint.domain.sprint.SprintRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class PersistSprintWaypoint implements Waypoint { + + private final SprintRepository sprintRepository; + + @Override + public Sprint handle(Sprint sprint, ExecutionContext context) { + return sprintRepository.save(sprint); + } +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java new file mode 100644 index 0000000..4ba0e30 --- /dev/null +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java @@ -0,0 +1,57 @@ +package io.github.scitia.app.sprint.flow.plan.waypoint; + +import io.github.scitia.aleatoricism.flows.api.Waypoint; +import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; +import io.github.scitia.app.sprint.domain.issue.IssueDto; +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; +import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +public class ValidateSprintPlanningRequestWaypoint implements Waypoint { + + @Override + public SprintPlanningRequest handle(SprintPlanningRequest input, ExecutionContext context) { + if (input == null) { + throw new IllegalArgumentException("Sprint planning request cannot be null."); + } + if (!StringUtils.hasText(input.name())) { + throw new IllegalArgumentException("Sprint name cannot be empty."); + } + if (!StringUtils.hasText(input.goal())) { + throw new IllegalArgumentException("Sprint goal cannot be empty."); + } + if (CollectionUtils.isEmpty(input.issues())) { + throw new IllegalArgumentException("Sprint must have at least one issue."); + } + for (IssueDto issue : input.issues()) { + validateIssue(issue); + } + + context.getStore().setSprintPlanningRequest(input); + + return input; + } + + private void validateIssue(IssueDto issue) { + if (issue == null) { + throw new IllegalArgumentException("Issue cannot be null."); + } + if (!StringUtils.hasText(issue.name())) { + throw new IllegalArgumentException("Issue name cannot be empty."); + } + if (!StringUtils.hasText(issue.description())) { + throw new IllegalArgumentException("Issue description cannot be empty."); + } + if (!StringUtils.hasText(issue.reporter())) { + throw new IllegalArgumentException("Issue reporter cannot be empty."); + } + if (issue.estimation() == null || issue.estimation() <= 0) { + throw new IllegalArgumentException("Issue estimation must be a positive number."); + } + if (CollectionUtils.isEmpty(issue.acceptanceCriteria())) { + throw new IllegalArgumentException("Issue must have at least one acceptance criterion."); + } + } +} + diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java index 201c058..dc03308 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/core/AleatoricCopilotGateway.java @@ -79,7 +79,7 @@ public AleatoricCommandResponse execute(@Valid AleatoricCommandRequest request) var done = new CompletableFuture(); session.on(SessionIdleEvent.class, _ -> done.complete(null)); - session.send(new MessageOptions().setPrompt(buildAgentPrompt(request))).get(); + session.sendAndWait(new MessageOptions().setPrompt(buildAgentPrompt(request))).get(); done.get(); } catch (InterruptedException exception) { Thread.currentThread().interrupt(); @@ -124,27 +124,24 @@ private String buildAgentPrompt(AleatoricCommandRequest request) { return """ - You are an AI agent that MUST use one of the available tools to handle the request. - - AVAILABLE TOOLS: - %s - - USER INTENT: - %s - - INPUT DATA: - %s - - IMPORTANT: - - You MUST call a tool - - Do NOT answer directly - - Select the most appropriate tool - - Return ONLY tool execution - - """.formatted( + You are an execution planner for business tools. + Choose tool(s) from the catalog and invoke them with proper JSON arguments. + You may call multiple tools if needed, but only if each call adds business value. + If no tool can safely satisfy intent, fail with a clear exception. + + Tool catalog: + %s + + Intent: %s + Instructions: %s + Payload: %s + Metadata: %s + """.formatted( toolsSummary, safeText(request.intent()), - payloadJson + safeText(request.instructions()), + payloadJson, + metadataJson ); } diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java index c17c6e9..d423229 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/config/aleatoricism/tools/app/agile/AgileSprintPlanningToolHandler.java @@ -4,15 +4,14 @@ import com.github.copilot.sdk.json.ToolHandler; import com.github.copilot.sdk.json.ToolInvocation; import io.github.scitia.aleatoricism.flows.engine.FlowEngine; -import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; import io.github.scitia.app.sprint.domain.sprint.Sprint; -import io.github.scitia.app.sprint.flow.SprintFlows; import io.github.scitia.app.sprint.domain.sprint.SprintMapper; +import io.github.scitia.app.sprint.flow.SprintFlows; +import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; import io.github.scitia.config.aleatoricism.tools.AgenticBusinessTool; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -55,8 +54,7 @@ public Map inputSchema() { ) ) - ), - "required", List.of("name", "goal") + ) ) ); } From aad891c370a7efa425fb721eb3378d9e31004cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Li=C5=BCewski?= Date: Sun, 14 Jun 2026 01:00:40 +0200 Subject: [PATCH 3/3] refactor process - VI stage --- .../aleatoricism/flows/api/Waypoint.java | 5 ++- .../app/sprint/domain/issue/IssueDto.java | 15 ++++--- .../scitia/app/sprint/flow/SprintFlows.java | 11 ++++- .../flow/api/SprintPlanningRequest.java | 9 +++- ...ValidateSprintPlanningRequestWaypoint.java | 45 ++----------------- 5 files changed, 35 insertions(+), 50 deletions(-) diff --git a/aleatoricism-flows/src/main/java/io/github/scitia/aleatoricism/flows/api/Waypoint.java b/aleatoricism-flows/src/main/java/io/github/scitia/aleatoricism/flows/api/Waypoint.java index 6657661..9fc9722 100644 --- a/aleatoricism-flows/src/main/java/io/github/scitia/aleatoricism/flows/api/Waypoint.java +++ b/aleatoricism-flows/src/main/java/io/github/scitia/aleatoricism/flows/api/Waypoint.java @@ -2,6 +2,7 @@ import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; import io.github.scitia.aleatoricism.flows.execution.FlowExecutionException; +import jakarta.validation.Valid; /** * Smallest executable unit in a business graph. @@ -9,9 +10,9 @@ @FunctionalInterface public interface Waypoint { - O handle(I input, ExecutionContext context) throws Exception; + O handle(@Valid I input, ExecutionContext context) throws Exception; - default O execute(I input, ExecutionContext context) { + default O execute(@Valid I input, ExecutionContext context) { try { return handle(input, context); } catch (FlowExecutionException exception) { diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java index e3a0752..61b636a 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/domain/issue/IssueDto.java @@ -1,12 +1,17 @@ package io.github.scitia.app.sprint.domain.issue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; + import java.util.List; public record IssueDto( - String name, - String description, - String reporter, - Integer estimation, - List acceptanceCriteria + @NotNull @NotBlank String name, + @NotNull @NotBlank String description, + @NotNull @NotBlank String reporter, + @Positive Integer estimation, + @NotEmpty List acceptanceCriteria ) { } diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java index a8a0501..12c4c1c 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/SprintFlows.java @@ -1,5 +1,6 @@ package io.github.scitia.app.sprint.flow; +import io.github.scitia.aleatoricism.flows.annotation.BusinessFlow; import io.github.scitia.aleatoricism.flows.engine.BusinessFlowBuilder; import io.github.scitia.aleatoricism.flows.engine.Flow; import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; @@ -18,11 +19,19 @@ public class SprintFlows { private final PersistSprintWaypoint persistSprintWaypoint; + private final ValidateSprintPlanningRequestWaypoint validationWaypoint; @Bean(name = "sprint-planning-flow") + @BusinessFlow( + name = "sprint-planning-flow", + description = """ + Flow to plan a new sprint, including validation, + creation, and persistence of the sprint and its associated issues. + """ + ) public Flow sprintPlanningFlow() { return BusinessFlowBuilder.define() - .start(new ValidateSprintPlanningRequestWaypoint(), SprintPlanningStore::new) + .start(validationWaypoint, SprintPlanningStore::new) .then(new CreateSprintEntityWaypoint()) .then(new AddIssuesToSprintWaypoint()) .then(persistSprintWaypoint) diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java index dcbcacd..8515648 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/api/SprintPlanningRequest.java @@ -1,9 +1,16 @@ package io.github.scitia.app.sprint.flow.api; import io.github.scitia.app.sprint.domain.issue.IssueDto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import java.util.List; -public record SprintPlanningRequest(String name, String goal, List issues) { +public record SprintPlanningRequest( + @NotNull @NotBlank String name, + @NotNull @NotBlank String goal, + @NotEmpty List issues +) { } diff --git a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java index 4ba0e30..2ef1f1a 100644 --- a/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java +++ b/aleatoricism-spring-boot-sample-app/src/main/java/io/github/scitia/app/sprint/flow/plan/waypoint/ValidateSprintPlanningRequestWaypoint.java @@ -2,56 +2,19 @@ import io.github.scitia.aleatoricism.flows.api.Waypoint; import io.github.scitia.aleatoricism.flows.execution.ExecutionContext; -import io.github.scitia.app.sprint.domain.issue.IssueDto; import io.github.scitia.app.sprint.flow.api.SprintPlanningRequest; import io.github.scitia.app.sprint.flow.plan.store.SprintPlanningStore; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +@Service +@Validated public class ValidateSprintPlanningRequestWaypoint implements Waypoint { @Override public SprintPlanningRequest handle(SprintPlanningRequest input, ExecutionContext context) { - if (input == null) { - throw new IllegalArgumentException("Sprint planning request cannot be null."); - } - if (!StringUtils.hasText(input.name())) { - throw new IllegalArgumentException("Sprint name cannot be empty."); - } - if (!StringUtils.hasText(input.goal())) { - throw new IllegalArgumentException("Sprint goal cannot be empty."); - } - if (CollectionUtils.isEmpty(input.issues())) { - throw new IllegalArgumentException("Sprint must have at least one issue."); - } - for (IssueDto issue : input.issues()) { - validateIssue(issue); - } - context.getStore().setSprintPlanningRequest(input); - return input; } - - private void validateIssue(IssueDto issue) { - if (issue == null) { - throw new IllegalArgumentException("Issue cannot be null."); - } - if (!StringUtils.hasText(issue.name())) { - throw new IllegalArgumentException("Issue name cannot be empty."); - } - if (!StringUtils.hasText(issue.description())) { - throw new IllegalArgumentException("Issue description cannot be empty."); - } - if (!StringUtils.hasText(issue.reporter())) { - throw new IllegalArgumentException("Issue reporter cannot be empty."); - } - if (issue.estimation() == null || issue.estimation() <= 0) { - throw new IllegalArgumentException("Issue estimation must be a positive number."); - } - if (CollectionUtils.isEmpty(issue.acceptanceCriteria())) { - throw new IllegalArgumentException("Issue must have at least one acceptance criterion."); - } - } }