# 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 --version` and 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 -DskipTests` inside `backend/` and confirm `target/ai-teacher-backend-*.jar` is 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 `` skeleton to `backend/pom.xml` and wire `spring-boot-maven-plugin` `process-aot` goal to the `prepare-package` phase inside that profile - [ ] T004 Add `native-maven-plugin` 0.10.6 to the `native` profile in `backend/pom.xml` with executions `add-reachability-metadata` and `compile-no-fork` (phase `package`), image name `ai-teacher-backend`, and build args `--initialize-at-build-time=org.slf4j` and `-H:+ReportExceptionStackTraces` - [ ] T005 [P] Create `backend/src/main/java/com/aiteacher/config/NativeHintsConfig.java` implementing `RuntimeHintsRegistrar`: register resource pattern `org/apache/pdfbox/resources/**` and reflection for `org.apache.pdfbox.pdmodel.font.encoding.GlyphList` with `MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS` - [ ] T006 Add `@ImportRuntimeHints(NativeHintsConfig.class)` annotation to the main `@SpringBootApplication` class in `backend/src/main/java/com/aiteacher/` - [ ] T007 Run `mvn -Pnative package -DskipTests` inside `backend/` and confirm `target/ai-teacher-backend` native 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-plugin` 3.4.5 to `backend/pom.xml` `` section (default build, not inside `native` profile): configure `gcr.io/distroless/base-nossl-debian12`, `ai-teacher-backend`, exposed port `8080`, and `` referencing `com.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension`; add `jib-native-image-extension-maven` 0.1.0 as plugin `` - [ ] T009 [US1] Run `mvn -Pnative package jib:dockerBuild -DskipTests` inside `backend/` and confirm `docker images | grep ai-teacher-backend` shows 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" and `GET /api/v1/books` returns 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 each `MissingResourceException` or `ClassNotFoundException` found, add the corresponding entry to `backend/src/main/java/com/aiteacher/config/NativeHintsConfig.java` and 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`) inside `backend/` still produces `target/ai-teacher-backend-*.jar`; confirm the jar starts correctly with `java -jar` - [ ] T013 [P] [US2] Verify `docker build -t ai-teacher-backend-jvm backend/` using the existing `backend/Dockerfile` succeeds and the resulting JVM container starts and serves `GET /api/v1/books` correctly **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.yml` at repo root: include `postgres` service (same image and config as `docker-compose.yml`), add `backend` service using image `ai-teacher-backend:latest` with all required env vars sourced from `.env`, `depends_on` postgres with health-check condition, and port mapping `8080:8080` - [ ] T015 [P] [US3] Create `.env.example` at 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.md` at 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.md` with 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 ```bash # 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 ```bash # 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) 1. Complete Phase 1: Setup (T001, T002) 2. Complete Phase 2: Foundational (T003–T007) — **CRITICAL, do not skip T007** 3. Complete Phase 3: US1 (T008–T011) 4. **STOP and VALIDATE**: Native image starts < 1 s, all endpoints work 5. This alone satisfies FR-001 through FR-006 and SC-001 through SC-003 ### Incremental Delivery 1. Phase 1 + Phase 2 → build toolchain ready 2. Phase 3 (US1) → native Docker image working (**MVP**) 3. Phase 4 (US2) → JVM fallback confirmed 4. Phase 5 (US3) → full native stack via docker compose 5. 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 `.env` must be in `.gitignore`