Skip to main content
ElastiCORE 3.0 is a release candidate: 3.0.261022-rc1 on Maven Central. Projects on the 2.2 line: see the 2.2 documentation.
Version: 3.0

A worked session: ask, DSL, build, run, call

1. Ask​

In an empty folder, start Claude Code with the kit installed and ask for an application "with ElastiCORE":

Build a REST API for a library with ElastiCORE: books with a 13-digit ISBN, members with a unique e-mail, loans the server dates, a 409 when no copy of a book is left, a return operation. Build it, start it on port 18080, check it, stop it.

In the runs the kit measured, the agent then read the skill's reference and the recipe that fits, wrote the YAML, generated and built with the Gradle wrapper, started the jar in the background, sent its requests - the 400s, 404s and 409s among them - stopped the server by its process id and reported, naming what it had not checked. Normally the kit sets a project up from the newest ElastiCORE release on Maven Central, with the template its CI builds every week; the measured runs used a pre-release build, 3.0.261021, from the local Maven repository.

2. The project and the DSL​

From new-project.md: settings.gradle (Maven Central for plugins), the Gradle wrapper (gradle --no-daemon wrapper --gradle-version 8.14.3, after settings.gradle, before build.gradle), build.gradle with architecture = 'hexagonal', an application class. Then the library recipe's model:

src/main/resources/blueprint/library/model/entity.yml
entity:
Book:
meta: entity @root @editable @expose(20) @sort(title+) -- book
fields:
isbn: string(13)! @id @pattern('^[0-9]{13}$', 'ISBN must be 13 digits') -- ISBN, 13 digits
title: string(200)! @notblank @search(like) -- title
author: string(100)! @notblank @search(like) -- author
pubYear: int @min(1450) @max(2100) -- publication year
copies: int! @min(0) -- copies owned

Member:
meta: entity @root @editable @expose(20) @sort(memberId+) @Index(name="uk_member_email", columnList="email", unique=true) -- member
fields:
memberId: long @id @sequence
name: string(50)! @notblank -- name
email: string(100)! @email -- e-mail address
joinedAt: date @readonly @default(now) -- joined on, set by the server

Loan:
meta: entity @root @editable @expose(20) @sort(loanId+) -- loan
fields:
loanId: long @id @sequence
member: Member! -- the borrower
book: Book! -- the book lent out
loanDate: date @readonly @default(now) -- lent on, set by the server
dueDate: date @readonly -- due back, loan date + 14
returnedDate: date @readonly @search(isnull) -- returned on, empty until returned

The rule that reads other rows (no copy left - ConflictException, 409), the due date in beforeSave, and the return operation are a few methods in LoanService plus one small controller.

3. Build​

./gradlew.bat compileJava      # on Windows, from Git Bash too; ./gradlew elsewhere

The hooks tell the agent after the YAML edit to generate and compile, and do not let it finish until a successful build has done both. The agent reads the whole output: the summary (=== elcore3: ... written / ... unchanged / ... skipped / 0 failed ===), no line under [elcore3] DSL validation warnings:, no * LOAD ERROR:.

4. Run - in the background, by process id​

./gradlew.bat bootJar
JAR=$(ls build/libs/*.jar | grep -v -- -plain.jar | head -1)
mkdir -p build/tmp
java -Djava.io.tmpdir="$PWD/build/tmp" -jar "$JAR" --server.port=18080 < /dev/null > build/app.log 2>&1 &
echo $! > build/app.pid
for i in $(seq 1 90); do
kill -0 "$(cat build/app.pid)" 2>/dev/null || { echo "the server stopped:"; tail -30 build/app.log; break; }
curl -s -o /dev/null localhost:18080/v3/api-docs && break; sleep 1
done

Never ./gradlew bootRun from a tool's shell (it does not return); start the server in one tool call and call it in the next ones; keep scratch files under build/.

5. Call - the refusals included​

From the library recipe's table of requests and answers:

OperationRequestAnswer
An ISBN of 12 or 14 digits, or with a letterPOST /api/library/book {"isbn":"978000000000",...}400, fieldErrors: isbn, "ISBN must be 13 digits"
An ISBN that existsthe same book again409, code already-exists
JoinPOST /api/library/member {"name":"Kim","email":"kim@example.com"}200 with memberId and joinedAt today; a joinedAt in the request is ignored
LendPOST /api/library/loan {"memberId":1,"bookIsbn":"9780000000001"}200 with loanDate today, dueDate today + 14
Lend without memberId{"bookIsbn":"9780000000001"}400, fieldErrors: memberId
Unknown member{"memberId":999999,"bookIsbn":"9780000000001"}404, "Member 999999 does not exist" (generated)
No copy lefta second loan of a book with 1 copy409, code conflict, "No copies of book 9780000000001 left: 1 of 1 are on loan"
ReturnPOST /api/library/loan/1/return200, returnedDate today
Return againthe same request409, "Loan 1 was already returned on " and the date
Not yet returnedPOST /api/library/loan/search {"memberId":1,"returnedDateEmpty":true}200, those loans
curl -s -X POST localhost:18080/api/library/loan/1/return
curl -s -X POST localhost:18080/api/library/loan/search -H 'Content-Type: application/json' -d '{"returnedDateEmpty":true}'

What to check, in this order: no DSL warnings and no LOAD ERROR; the server answers /v3/api-docs; every operation the task names answers as the task says - the 400s, 404s and 409s too.

6. Stop, report​

kill "$(cat build/app.pid)" && rm build/app.pid             # the server you started - never by name

The report starts with what changed for the business, then what was checked and how, then what was not checked. For an existing project, /elasticore:change-report writes it from the DSL diff.

What it took​

The same library task with and without the kit, graded on 21 checks by a separate agent (details):

AgentWithout the kitWith the kit
Claude Code, Sonnet4 min 41 s, 20.5 of 212 min 18 s, 20.5 of 21
Codex CLI, gpt-5.6-sol7 min 13 s, 21 of 213 min 45 s, 21 of 21
opencode, a small local modelno final report in four attempts11 min 42 s, 21 of 21

The library recipe was written from this task after the first round, so these with-kit runs had a recipe for the task's domain.