Skip to content

refactor(conference-scheduling): convert to Models Service SDK structure#1126

Open
larsbeck wants to merge 12 commits into
developmentfrom
chore/conference-scheduling-refactor
Open

refactor(conference-scheduling): convert to Models Service SDK structure#1126
larsbeck wants to merge 12 commits into
developmentfrom
chore/conference-scheduling-refactor

Conversation

@larsbeck

Copy link
Copy Markdown
Collaborator

Converts the conference-scheduling quickstart to the Models Service SDK structure:

  • Move solver justifications to domain/justification with a shared ConferenceSchedulingJustification interface
  • Add DTO layer (input/output, metrics, config overrides, validation issues)
  • Add DemoDataBuilder/DemoDataGenerator for demo data
  • Update pom.xml for platform model conventions

Review feedback incorporated

Addresses the review comments from @triceo, @TomCools and Copilot:

Less code, readable construction (net ~-970 lines)

  • Nested builders on domain Talk/Speaker and on TalkDTO/SpeakerDTO; the telescoping 22-/11-arg constructors and their PMD.ExcessiveParameterList suppressions are gone, so the PMD parameter-list rule is enforced again
  • All generated withXxx methods deleted except the three actually used; the ArchUnit wither-per-component rule (which manufactured the bloat) is removed, with_methods_must_be_used stays and now bans dead withers
  • Repeated normalization ternaries replaced by reused immutableCopy/orEmpty/nonNegative helpers
  • Static imports restored in the ConstraintProvider (PMD TooManyStaticImports raised to 30)
  • ConfigOverrides: the 20 withers are gone and the no-arg constructor now yields all-null "no overrides" (previously it silently overrode every weight with 1). The constructor itself must stay because the SDK requires it reflectively — tracked in Service SDK: ModelConfigOverrides records are forced to declare an all-null no-arg constructor timefold-solver#2493

Build & quality gates

  • Custom coverage harness (CoverageReportSummary, ShadowSourcesCoverageCheck, RecordWithMethodCoverageTest) removed — plain JaCoCo gates unchanged (30% instruction / 20% branch bundle, 100% line on the ConstraintProvider)
  • Node/npm fully out of the maven build; markdownlint removed (spotless still formats markdown)
  • The vacuum OpenAPI-description lint moved to CI: .github/workflows/lint_openapi.yml lints the committed spec of every model shipping a .vacuum-ruleset.yaml (covers future models automatically). src/build/openapi.json and .vacuum-ruleset.yaml are now actually committed — the root .gitignore's .* and build patterns had been silently excluding them, so the lint could never run before
  • Unused WebJars dependencies and quarkus-web-dependency-locator dropped (UI loads assets from CDN)
  • Parent set to 999-SNAPSHOT (replaced on release; CI builds the solver from source), JFrog repositories removed — everything resolves from Maven Central

Correctness

  • Model conversion fails fast with IllegalArgumentException("Unknown <kind> '<key>'.") on unknown talk-type/speaker/timeslot/room/prerequisite references instead of leaking nulls into the solver model
  • Validator rejects blank talkTypeName, not just null
  • ConferenceScheduleIssue got its missing OpenAPI description (vacuum scores 100/100); small UI/test fixes (unclosed span, stray jQuery .fail argument, assertNull), README cd path corrected

Note on CI: the failing Maven Long-Running jobs are pre-existing repo infrastructure breakage — actions/checkout v7 refuses fork-PR checkouts under pull_request_target (see this comment); unrelated to this PR.

🤖 Generated with Claude Code

larsbeck and others added 8 commits July 14, 2026 14:13
- Move solver justifications to domain/justification with shared
  ConferenceSchedulingJustification interface
- Add DTO layer (input/output/metrics/config overrides/validation)
- Add DemoDataBuilder/DemoDataGenerator for demo data
- Update pom.xml for platform model conventions
- replace generated withXxx bloat and telescoping constructors with builders
- fail fast on unknown references in model conversion
- drop node/npm from the maven build and custom coverage harness
- move to 999-SNAPSHOT parent, drop JFrog repositories

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Reverts the removal of the vacuum OpenAPI lint and markdownlint from the
maven build and drops the DtoSchemaDescriptionTest that stood in for them.
Also commits the previously missing .vacuum-ruleset.yaml and
.markdownlint-cli2.jsonc (the root .gitignore's ".*" pattern had silently
excluded them, so the lint steps could never run) and adds the missing
@Schema description on ConferenceScheduleIssue that the restored vacuum
lint caught.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Vacuum is a Go binary; the npm package was only a distribution channel.
Download the pinned platform binary from GitHub releases instead of
installing node/npm via frontend-maven-plugin. Markdownlint is removed
entirely (spotless keeps formatting markdown).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Moves the vacuum lint out of the maven build into a dedicated GitHub
workflow, following vacuum's documented CI/CD setup. The workflow lints
the committed spec of every model shipping a .vacuum-ruleset.yaml, so
future converted models are covered automatically. The generated
src/build/openapi.json is now committed (force-added past the root
.gitignore's "build" pattern) to keep the lint standalone and make spec
drift visible in diffs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Unknown prerequisite talk codes now fail fast like every other reference,
the dataset info span is closed properly, a stray jQuery .fail() argument
is removed and DemoDataBuilderTest uses assertNull.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
The UI loads all assets from CDNs, so the WebJars runtime dependencies
and the web-dependency-locator extension were dead weight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
…leset

The header still pointed at the removed exec-maven-plugin wiring; the
lint now runs in the lint_openapi.yml workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors the conference-scheduling quickstart to align with the Timefold Models Service SDK structure, introducing a DTO-first API surface and SDK-compatible model conversion/validation while simplifying build/CI and updating the UI to the new REST contract.

Changes:

  • Introduces a DTO layer (+ validation issues/metrics/config overrides) and a ConferenceScheduleModelConvertor/ConferenceScheduleValidator for SDK model I/O.
  • Replaces the bespoke REST resources with an SDK ModelRest interface and updates the UI + tests to use the /v1/... endpoints and ModelRequest/ModelResponse shapes.
  • Adds OpenAPI spec linting in CI via vacuum rulesets and commits the generated OpenAPI JSON.

Reviewed changes

Copilot reviewed 61 out of 63 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/SolverTestDataFactory.java Adds DTO-based test data factory for solver tests.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/SolverManagerTest.java Adds solver smoke test using model conversion + metrics assertions.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceSchedulingConstraintProviderTest.java Minor formatting adjustment in constraint provider tests.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceScheduleResourceIT.java Adds native IT targeting /v1 endpoints and async solving status.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceScheduleEnvironmentTest.java Updates env-mode test to use DTO input + convertor (no REST demo-data).
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/rest/ConferenceSchedulingResourceIT.java Removes old REST IT for legacy endpoints.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/rest/ConferenceScheduleResourceTest.java Removes old REST test for legacy endpoints.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/ProjectStructureTest.java Adds ArchUnit rules enforcing layered architecture + DTO/record conventions.
use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/demo/DemoDataBuilderTest.java Adds test coverage for new demo input builder.
use-cases/conference-scheduling/src/main/resources/META-INF/resources/index.html Switches UI assets to CDN and updates curl examples + platform embed hooks.
use-cases/conference-scheduling/src/main/resources/META-INF/resources/app.js Refactors UI to ModelRest contract, /v1 endpoints, and platform embedding params.
use-cases/conference-scheduling/src/main/resources/application.properties Adopts model metadata/settings + updated termination properties.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/justifications/ConferenceSchedulingJustification.java Removes old solver justification record (moved to domain justification).
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/ConstraintGroupTag.java Introduces tags for constraint group classification.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/ConferenceScheduleConstraintGroup.java Adds SDK-facing constraint group definitions.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleValidator.java Adds SDK ModelValidator producing structured validation issues.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleModelConvertor.java Adds SDK ModelConvertor between DTOs and solver domain model.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleIssues.java Defines concrete validation issue classes for the model.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ErrorInfo.java Removes legacy error payload type for old REST layer.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ConferenceScheduleSolverExceptionMapper.java Removes legacy exception mapper (old REST stack).
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ConferenceScheduleSolverException.java Removes legacy REST exception type.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/DemoDataGenerator.java Removes legacy demo-data generator that returned domain model directly.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceSchedulingResource.java Removes legacy bespoke REST resource implementation.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceSchedulingDemoResource.java Removes legacy demo-data REST resource.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceScheduleResource.java Adds SDK ModelRest resource interface for schedules.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TimeslotIdDetail.java Adds validation metadata record for timeslot ID issues.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TimeslotDTO.java Adds timeslot DTO for wire format.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkTypeDTO.java Adds talk type DTO for wire format.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkIdDetail.java Adds validation metadata record for talk ID issues.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkDTO.java Adds talk DTO + builder + minimal withers for solver output merging.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/SpeakerIdDetail.java Adds validation metadata record for speaker ID issues.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/SpeakerDTO.java Adds speaker DTO + builder for wire format.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/RoomIdDetail.java Adds validation metadata record for room ID issues.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/RoomDTO.java Adds room DTO for wire format.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleValidationIssue.java Enumerates validation issue types/codes for the model.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleOutputMetrics.java Adds structured output metrics DTO.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleOutput.java Adds model output DTO for API responses.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleInputMetrics.java Adds structured input metrics DTO.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleInput.java Adds model input DTO for API requests.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleConfigOverrides.java Adds nullable constraint-weight overrides per SDK conventions.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Timeslot.java Adjusts overlap semantics and formatting in equals/overlap helpers.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/TalkType.java Switches compatible sets to linked sets + formatting in equals.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Talk.java Replaces large constructors with builder and adds isScheduled().
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Speaker.java Replaces large constructors with builder.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Room.java Formatting/consistency updates in equals.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/UndesiredTagsJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/UnavailableTimeslotJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/RequiredTagsJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ProhibitedTagsJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/PreferredTagsJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/DiversityTalkJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ConflictTalkJustification.java Moves justification to domain + enforces non-null description.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ConferenceSchedulingJustification.java Adds shared justification record in domain package.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/ConferenceSchedule.java Adapts solution to SDK SolverModel, adds metrics + weight overrides.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/ConferenceConstraintProperties.java Removes redundant explicit no-arg constructor.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/demo/DemoDataGenerator.java Adds SDK demo-data generator returning ModelRequest.
use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/demo/DemoDataBuilder.java Adds deterministic demo dataset builder producing DTO input.
use-cases/conference-scheduling/README.md Updates CLI snippets and paths for consistency.
use-cases/conference-scheduling/pom.xml Migrates module to platform parent conventions + quality gates + OpenAPI spec copy.
use-cases/conference-scheduling/.vacuum-ruleset.yaml Adds vacuum ruleset enforcing schema/property descriptions for OpenAPI.
.github/workflows/lint_openapi.yml Adds CI job linting committed OpenAPI specs for models with vacuum rulesets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@oemebamo

Copy link
Copy Markdown
  • The diff is +5000 lines again, whereas previously it was net negative.

This is imo expected, and it's the direct result of that we want the use-cases folder to contain examples that are feature complete, platform compatible and well structured. Which comes at a cost.

  • Getting started: stays minimal. No DTOs, no metrics, no justifications. This is the "as little code as possible" experience, and it's not what this PR touches.
  • Use cases: deliberately full platform-featured: DTOs, input/output metrics, justifications, constraint weights. Simple in terms of # constraints, but not minimal in code. This is a conscious trade-off, not scope creep.

(We should keep pushing so "fully featured" doesn't have to mean "this much code" forever. But that's a follow-up?)

Error Prone requires -XDaddTypeAnnotationsToSymbol=true on JDK 21 (the
flag is ignored on newer JDKs). Also commits .pmd-ruleset.xml, which the
root .gitignore's ".*" pattern had silently excluded — the PMD check
could not run in CI without it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Copilot AI review requested due to automatic review settings July 14, 2026 13:44
@larsbeck

larsbeck commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author

@triceo @pieterdeschepper granted me permission now so I could create a new branch, hence the new PR.

We want these use-cases to be fully featured and be in the right "shape". They are not getting started experiences. They are in fact almost the opposite. Think of them as an opinionated way to build a production ready model (even though they are small models). When someone wants to build a production ready model, they should not need to think about what goes where, are there any building blocks missing - they should be able to take this and start modifying.

See #1126 (comment)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 62 out of 64 changed files in this pull request and generated 5 comments.

Comment thread use-cases/conference-scheduling/src/main/resources/application.properties Outdated
Copilot AI review requested due to automatic review settings July 14, 2026 13:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 62 out of 64 changed files in this pull request and generated 4 comments.

Comment on lines +71 to +73
Timeslot timeslot = new Timeslot(dto.id(), LocalDateTime.parse(dto.startDateTime()),
LocalDateTime.parse(dto.endDateTime()), talkTypes(dto.talkTypeNames(), talkTypeMap),
new LinkedHashSet<>(dto.tags()));
Comment thread use-cases/conference-scheduling/pom.xml
Comment thread use-cases/conference-scheduling/pom.xml
…heck

Replaces the in-module ArchUnit ProjectStructureTest (and the archunit
test dependency) with .github/scripts/ArchitectureCheck.java, a JBang
script running the identical rules against target/classes in the new
architecture_check.yml workflow. The workflow covers every Models
Service SDK quickstart automatically, so the guarantees are unchanged
while the model modules stay free of test scaffolding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Copilot AI review requested due to automatic review settings July 15, 2026 12:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 65 changed files in this pull request and generated 2 comments.

Comment on lines +6 to +11
return {
onPlatform: q.has('onPlatform'),
runId: q.get('runId'),
apiUrl: q.has('apiUrl') ? decodeURIComponent(q.get('apiUrl')).replace(/\/+$/, '') : null,
apiKey: q.has('apiKey') ? q.get('apiKey') : null,
};
Comment on lines 194 to 199
<h3>5. Fetch the analysis of the solution</h3>
<pre>
<button class="btn btn-outline-dark btn-sm float-end"
onclick="copyTextToClipboard('curl5')">Copy</button>
<code id="curl5">curl -X PUT -H 'Content-Type:application/json' http://localhost:8080/schedules/analyze -d@solution.json</code>
<code id="curl5">curl -X GET -H 'Accept:application/json' http://localhost:8080/v1/schedules/{jobId}/score-analysis</code>
</pre>
Copilot AI review requested due to automatic review settings July 15, 2026 12:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 65 changed files in this pull request and generated 2 comments.

Comment on lines +195 to +199
SequencedSet<Talk> prerequisites = dto.prerequisiteTalkCodes().stream()
.map(code -> require(talkMap, code, "prerequisite talk"))
.collect(toCollection(LinkedHashSet::new));
talkMap.get(dto.code()).setPrerequisiteTalks(prerequisites);
}
Comment on lines +164 to +167
}).fail(function (xhr) {
showError("Getting the schedule has failed.", xhr);
refreshSolvingButtons("SOLVING_COMPLETED");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants