MIP-0008 tasks¶
Ordered delivery of MIP-0008 as stacked PRs
(.claude/skills/mip-tasks/SKILL.md, scripts/stack.sh). Branch mip-0008/<k>-<slug>, each based
on the previous; merge bottom-up, scripts/stack.sh restack after each squash-merge.
| # | slug | delivers | tests (must exist before the PR) | depends on |
|---|---|---|---|---|
| 1 | maps-url | Coordinates.fromMapsUrl (core, pure: /@lat,lon, q=/query=/ll=, !3dlat!4dlon), --location-url in Main (after --lat/--lon, before the env vars; an unreadable URL warns and falls through), docs (ARCHITECTURE.md §3.1, RUN-LOCALLY.md §4), this task list, MIP → Accepted |
CoordinatesSpec (core): the four URL shapes, a URL-encoded comma, the pin winning over the viewport, garbage/non-Google/out-of-range → None, a short link → None; live: just run -- --brief --location-url <place url> prints source: --location-url |
– |
| 2 | docker-jvm | Dockerfile (builder: sbt cli/assembly; jvm: Temurin 25 JRE alpine + the jar; dev: nixos/nix with the flake's shell warmed), .dockerignore, docker-compose.yml (marola on :jvm, ollama + one-shot ollama-pull, profiles ollama and local, .env mounted), just docker-build/docker-run/docker-compose, hadolint in just quality (skipped when not installed, like ruff), RUN-LOCALLY.md §11 "Docker only", README quick-start row, ARCHITECTURE.md §11 Phase 3 |
hadolint Dockerfile (in just quality); docker build --target jvm then docker run … --brief --lat -27.6733 --lon -48.47 live and docker compose --profile ollama run marola --summarize … (this needs a Docker daemon, which the agent's session does not have: run on the host before merging, or let task 4's workflow be the first build) |
1 |
| 3 | docker-native | sbt-native-image 0.5.0 on cli (nativeImageInstalled, --initialize-at-build-time=org.slf4j,ch.qos.logback,com.fasterxml.jackson, -march=compatibility), reachability metadata under cli/src/main/resources/META-INF/native-image/ (the root-level *.json resources: the two compiled prompts, sea_lore.json, benchmark_questions.json; sun.misc.Signal reflection for Kyo's signal handler), just native-image (GraalVM from nix shell nixpkgs#graalvmPackages.graalvm-ce, not added to the flake, 700 MB), Dockerfile stages native-build (ghcr.io/graalvm/native-image-community:25) and native (gcr.io/distroless/base-debian12:nonroot), docs |
sbt cli/nativeImage locally; the binary runs --brief and --summarize (Ollama, llama3.2:1b) live with the same output as the JVM; just test unchanged (the golden suite stays on the JVM, MIP §7) |
2 |
| 4 | docker-ci | .github/workflows/docker.yml: on PR → hadolint + build :jvm (no push, GHCR cache); on main → build and push :jvm (amd64+arm64, jar built once on the build platform), :native (amd64), :dev to ghcr.io/h0ffmann/marola, each also tagged <target>-<sha>; docker/metadata-action + docker/build-push-action with GITHUB_TOKEN (packages: write); docs: RUN-LOCALLY.md §11 uses the published tags |
actionlint (just quality); the PR's own run builds :jvm; first push run after merge |
3 |
| 5 | smoke-workflow | .github/workflows/docker-smoke.yml (workflow_dispatch + daily 09:30 UTC): inputs lat, lon, maps_url, model (llama3.2:1b), image (ghcr.io/h0ffmann/marola:jvm); short link → curl -sIL → --location-url, else --lat/--lon, else Campeche; Ollama binary + model cached as in marola-e2e.yml; docker run --network host $image --summarize …; scripts/smoke_record.py turns the stdout into smoke/<run-id>.json + smoke/latest.json + smoke/history.json (50 runs) committed to the orphan site-data branch (contents: write), artifact uploaded, site.yml triggered (actions: write) |
python3 scripts/smoke_record.py --self-test (a recorded --summarize stdout → top pick, draft, reviewer score/verdict, exit code; a failed run → ok: false and no summary) in just quality; actionlint; one manual run after merge |
4 |
| 6 | smoke-panel | site.yml: check out site-data (if it exists) into site/dist/smoke/; app.js: fetch smoke/latest.json if present → "Last live run" panel in the footer (when, where, model, image, top pick, the summary marked as LLM text, the reviewer's score/verdict, a link to the run; verdict reject → numbers only) + a marker at the run's origin; smoke/history.json → the last 10 runs; style.css; MIP §8 "public model text" wording in the panel |
manual (just site-build floripa, drop a sample smoke/latest.json into site/dist/smoke/, just site-serve, see the panel and the marker); actionlint; SiteBuilderSpec unchanged |
5 |
| 7 | marola-local | Dockerfile.local (ollama/ollama + a model-store layer with marola-llama3.2 created from finetune/Modelfile at build time, Tier 1), compose profile local pointing at it, docker.yml job local (on main and workflow_dispatch): build, push :local and :local-<sha>, then the benchmark gate: just benchmark against the image's Ollama and scripts/benchmark_gate.py comparing the summary table with docs/benchmarks/: fails when rag-general coverage (all) drops below the kept run's or below baseline's; finetune/README.md "as an image" section; MIP → Implemented after merge |
python3 scripts/benchmark_gate.py --self-test (parses the kept docs/benchmarks/2026-09-05.md, passes on it, fails on a doctored copy) in just quality; actionlint; one workflow_dispatch run after merge |
6 |
Decisions taken for v1 (MIP §11), so that the tasks above are unambiguous:
- Native-image: yes. The spike (2026-09-05, GraalVM CE 25.2.4 = JDK 25.0.4, sbt-native-image
0.5.0, Kyo 1.0.0-RC5, Scala 3.9.0): the image builds in 44 s once slf4j/logback/Jackson are
initialised at build time (the first two failures were exactly those, reached through
a static logger and the MCP SDK's Jackson); the 69 MB binary runs the live
--briefpath in 35 s wall / 75 MB RSS (all of it Overpass/Open-Meteo).--summarizeneeds the compiled prompts as resources: metadata, task 3. Kyo itself needed nothing. The MCP server stays JVM (MIP §4). :jvmbase:eclipse-temurin:25-jre-alpine(71 MB, 2026-08-21;25-jreis 111 MB, both checked on Docker Hub 2026-09-05). Nojlinkfor v1: the 40 MB it might save is not worth a second build path. Builder:sbtscala/scala-sbt:eclipse-temurin-25.0.4_7_1.13.0_3.8.4(466 MB, 2026-09-05). Native builderghcr.io/graalvm/native-image-community:25, runtimegcr.io/distroless/base-debian12:nonroot(both manifests answered 200 on 2026-09-05).- Smoke test cadence: daily at 09:30 UTC (06:30 in Brazil, before the morning swim) plus on demand. Never per PR (MIP §8: ~5 min of runner per run).
- Where the smoke result lives: the orphan
site-databranch, copied intosite/dist/smoke/bysite.yml; the smoke workflow never deploys Pages itself (MIP §5.5). - Tier 2 adapter: not in v1.
marola-localis the Tier 1 Modelfile model onllama3.2(finetune/README.mdsays so already); an adapter shipped as a release asset is a later task, once something has trained it. - The panel is on the public map, in the footer, labelled as a live run of the model with the
reviewer's verdict next to it; a
rejectverdict hides the sentence and keeps the numbers. A separate/status.htmlis not worth a second page for one panel. - Platforms:
:jvmand:devforlinux/amd64andlinux/arm64(the jar is built once, on the build platform, and copied into each);:nativeand:localforlinux/amd64only (native-image does not cross-compile; the Ollama model store is platform-independent but the base image build is done once). - Ollama in compose is the official
ollama/ollama:latest(3.5 GB, 2026-09-03), pulled once into a named volume; CI uses the ~100 MB binary asmarola-e2e.ymldoes. - Benchmark gate metric:
rag-generalcoverage (all) from the summary table, compared with the best table of the newest file indocs/benchmarks/(that file keeps several runs; the best is run 2's 0.84): a drop of more than 0.05, or falling below the new run's ownbaseline, fails the job and:localis not moved. One number, the onedocs/benchmarks/2026-09-05.mdcalls "the result that matters". Verified on the last local run (data/benchmark-20260905-0950.md: 0.83 vs 0.84, baseline 0.73 → pass).
Restack notes: task 3 appends stages to task 2's Dockerfile and adds keys to build.sbt (task
1 does not touch it); task 7 replaces the local profile's services in task 2's compose file (its own
workflow, docker-local.yml, leaves task 4's untouched). Nothing else rewrites a file an earlier
task created; tasks 5 and 6 touch only files that are already on main (MIP-0005's page and
workflow) or new ones.