commit 0d624137c2557c6eeb87020749e4977b821c2b5c Author: Adrien <adrien.cesaro@proton.me> Date: Thu Apr 9 11:55:22 2026 +0200 backend native image setup
11 KiB
Tasks: Native Image Deployment
Input: Design documents from /specs/005-native-image-deployment/
Prerequisites: plan.md ✅, spec.md ✅, research.md ✅, data-model.md ✅, contracts/ ✅
Tests: No test tasks — this feature has no unit/integration test requirements in the spec. Smoke testing is part of US1 implementation (T010, T011).
Organization: Tasks grouped by user story for independent implementation and verification.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no incomplete-task dependencies)
- [Story]: Which user story this task belongs to (US1, US2, US3)
- Paths use
backend/and repo root (web app structure per constitution)
Phase 1: Setup (Shared Infrastructure)
Purpose: Verify prerequisites and baseline before touching any build files.
- T001 Verify GraalVM 25 CE or Oracle GraalVM 25 is installed: run
native-image --versionand confirm output shows GraalVM 25; document install command (sdk install java 25-graalce) if missing - T002 [P] Verify baseline JVM build passes: run
mvn package -DskipTestsinsidebackend/and confirmtarget/ai-teacher-backend-*.jaris produced
Checkpoint: GraalVM available, existing JVM build green — safe to modify build files
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Add AOT processing and native compilation support to the Maven build. These changes are prerequisites for all user stories.
⚠️ CRITICAL: US1 cannot start until T007 confirms the native executable is produced.
- T003 Add
<profile id="native">skeleton tobackend/pom.xmland wirespring-boot-maven-pluginprocess-aotgoal to theprepare-packagephase inside that profile - T004 Add
native-maven-plugin0.10.6 to thenativeprofile inbackend/pom.xmlwith executionsadd-reachability-metadataandcompile-no-fork(phasepackage), image nameai-teacher-backend, and build args--initialize-at-build-time=org.slf4jand-H:+ReportExceptionStackTraces - T005 [P] Create
backend/src/main/java/com/aiteacher/config/NativeHintsConfig.javaimplementingRuntimeHintsRegistrar: register resource patternorg/apache/pdfbox/resources/**and reflection fororg.apache.pdfbox.pdmodel.font.encoding.GlyphListwithMemberCategory.INVOKE_PUBLIC_CONSTRUCTORS - T006 Add
@ImportRuntimeHints(NativeHintsConfig.class)annotation to the main@SpringBootApplicationclass inbackend/src/main/java/com/aiteacher/ - T007 Run
mvn -Pnative package -DskipTestsinsidebackend/and confirmtarget/ai-teacher-backendnative executable is produced; fix any AOT compilation errors before proceeding
Checkpoint: target/ai-teacher-backend exists and exits cleanly — US1 implementation can begin
Phase 3: User Story 1 — Build Native Docker Image (Priority: P1) 🎯 MVP
Goal: One command (mvn -Pnative package jib:dockerBuild) produces a local Docker image
containing the native backend. Container starts in < 1 s and all REST endpoints work.
Independent Test: Start native container with DB env vars → GET /api/v1/books returns 200 →
startup log shows ready in under 1 second.
Implementation for User Story 1
- T008 [US1] Add
jib-maven-plugin3.4.5 tobackend/pom.xml<build><plugins>section (default build, not insidenativeprofile): configure<from><image>gcr.io/distroless/base-nossl-debian12</image></from>,<to><image>ai-teacher-backend</image></to>, exposed port8080, and<pluginExtensions>referencingcom.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension; addjib-native-image-extension-maven0.1.0 as plugin<dependency> - T009 [US1] Run
mvn -Pnative package jib:dockerBuild -DskipTestsinsidebackend/and confirmdocker images | grep ai-teacher-backendshows the produced image - T010 [US1] Start the native container with required env vars (
SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAME,SPRING_DATASOURCE_PASSWORD,OPENAI_API_KEY) pointing to a local PostgreSQL; verify startup log shows "Started … in < 1.0 seconds" andGET /api/v1/booksreturns HTTP 200 - T011 [US1] Run full smoke test against the running native container:
POST /api/v1/books(PDF upload),POST /api/v1/chat(RAG query),GET /api/v1/chat/history, and HTTP Basic auth enforcement; for eachMissingResourceExceptionorClassNotFoundExceptionfound, add the corresponding entry tobackend/src/main/java/com/aiteacher/config/NativeHintsConfig.javaand rebuild (T009 → T010 loop) until all pass
Checkpoint: Native Docker image starts < 1 s, all 5 endpoints work — US1 complete ✅
Phase 4: User Story 2 — JVM Mode Preserved (Priority: P2)
Goal: Default Maven build and existing Dockerfile continue to work with no changes required from developers who do not have GraalVM installed.
Independent Test: mvn package -DskipTests produces a fat-jar; docker build using
backend/Dockerfile produces a working JVM container.
Implementation for User Story 2
- T012 [P] [US2] Verify
mvn package -DskipTests(no-Pnative) insidebackend/still producestarget/ai-teacher-backend-*.jar; confirm the jar starts correctly withjava -jar - T013 [P] [US2] Verify
docker build -t ai-teacher-backend-jvm backend/using the existingbackend/Dockerfilesucceeds and the resulting JVM container starts and servesGET /api/v1/bookscorrectly
Checkpoint: JVM path fully unchanged — US2 complete ✅
Phase 5: User Story 3 — Docker Compose Full Stack (Priority: P3)
Goal: docker compose -f docker-compose.native.yml up starts PostgreSQL + native backend
together so developers can run the complete native stack locally with a single command.
Independent Test: docker compose -f docker-compose.native.yml up → all services healthy →
GET http://localhost:8080/api/v1/books returns 200.
Implementation for User Story 3
- T014 [US3] Create
docker-compose.native.ymlat repo root: includepostgresservice (same image and config asdocker-compose.yml), addbackendservice using imageai-teacher-backend:latestwith all required env vars sourced from.env,depends_onpostgres with health-check condition, and port mapping8080:8080 - T015 [P] [US3] Create
.env.exampleat repo root listing all env vars needed by the native stack (SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAME,SPRING_DATASOURCE_PASSWORD,OPENAI_API_KEY,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION) with placeholder values and inline comments
Checkpoint: Full native stack starts via docker compose — US3 complete ✅
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Constitution IV gate (README must be updated in the same PR as architectural change) and final documentation alignment.
- T016 Update
README.mdat repo root: add a "Native Image Build" section with GraalVM install command (sdk install java 25-graalce), build command (mvn -Pnative package jib:dockerBuild), and run command (docker compose -f docker-compose.native.yml up); update the Mermaid architecture diagram to show the native build pipeline (Maven native profile → GraalVM native-image → Jib → Docker image) - T017 [P] Update
specs/005-native-image-deployment/quickstart.mdwith any corrections or additions discovered during implementation (env var names, exact commands, troubleshooting entries)
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies — start immediately
- Foundational (Phase 2): Requires Phase 1 completion — BLOCKS all user stories
- US1 (Phase 3): Requires Phase 2 (T007 must pass) — primary deliverable
- US2 (Phase 4): Requires Phase 2 (T003–T006 must not break JVM path) — can run in parallel with US1 after Phase 2
- US3 (Phase 5): Requires US1 completion (T009 must produce the Docker image)
- Polish (Phase 6): Requires US1, US2, US3 all complete
User Story Dependencies
- US1 (P1): Unblocked after Foundational — critical path
- US2 (P2): Unblocked after Foundational — can run in parallel with US1 (different verification commands, no file conflicts)
- US3 (P3): Depends on US1 (needs the Docker image to exist for docker-compose)
Within US1
T008 (Jib config) → T009 (build image) → T010 (verify startup) → T011 (smoke test + hint fixes, may loop back to T009)
Parallel Opportunities
- T001 and T002 (Phase 1): both can run in parallel
- T003/T004 (pom.xml edits) are sequential (same file); T005 (new Java file) can run in parallel with T003/T004
- T012 and T013 (US2 verification): parallel, different commands
- T014 and T015 (US3 docker-compose + .env.example): parallel, different files
- T016 and T017 (Polish): parallel, different files
- US1 (Phase 3) and US2 (Phase 4) can run in parallel after Foundational completes
Parallel Example: Foundational Phase
# In parallel — different files, no dependencies between them:
Task T005: "Create NativeHintsConfig.java in backend/src/main/java/com/aiteacher/config/"
# Sequential — same file (pom.xml):
Task T003: "Add native profile skeleton with spring-boot-maven-plugin process-aot"
Task T004: "Add native-maven-plugin 0.10.6 to native profile" # after T003
Parallel Example: After Foundational Completes
# US1 and US2 can start simultaneously:
Developer A → Phase 3 (US1): T008 → T009 → T010 → T011
Developer B → Phase 4 (US2): T012, T013 (parallel)
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Setup (T001, T002)
- Complete Phase 2: Foundational (T003–T007) — CRITICAL, do not skip T007
- Complete Phase 3: US1 (T008–T011)
- STOP and VALIDATE: Native image starts < 1 s, all endpoints work
- This alone satisfies FR-001 through FR-006 and SC-001 through SC-003
Incremental Delivery
- Phase 1 + Phase 2 → build toolchain ready
- Phase 3 (US1) → native Docker image working (MVP)
- Phase 4 (US2) → JVM fallback confirmed
- Phase 5 (US3) → full native stack via docker compose
- Phase 6 (Polish) → README updated (constitution IV gate — required before merge)
Notes
- [P] tasks operate on different files or independent commands — safe to run concurrently
- US2 (T012, T013) can be verified at any time after Phase 2; they are quick sanity checks
- The hint-fix loop in T011 is the most likely source of iteration — budget extra time
- Constitution IV gate (README update, T016) is mandatory — PR cannot merge without it
.env.example(T015) should be committed; actual.envmust be in.gitignore