MIP-0031 tasks¶
Written by hand, following .claude/skills/mip-tasks/SKILL.md's Step 1 process. That skill has
disable-model-invocation: true, so this file was produced without invoking it as a slash command;
the process it documents was followed manually instead.
Both research gaps that blocked design (§11: INEA's real bulletin layout, whether INEMA's search form offers an HTML shortcut) were resolved 2026-09-07; see the MIP's own §11 for what was verified live and how. This file is the resulting build plan.
Progress (verified against origin/main 2026-09-07): tasks 1/3 merged as PR #203
(InemaPdfParser, sampling_points_ba.json); tasks 2/4 merged as PR #200 (IneaPdfParser,
sampling_points_rj.json). Tasks 5/6 (the actual WaterQualityClient wiring into AppConfig)
have not landed: InemaBaWaterQualityClient/IneaRjWaterQualityClient do not exist on main yet,
so beaches in Rio/Bahia still get "no data" for water quality despite the parsers/coordinate
tables existing.
| # | slug | delivers | tests (must exist before the PR) | depends on |
|---|---|---|---|---|
| 1 | pdf-parser-inema | ✅ merged (PR #203): local/src/main/scala/marola/water/InemaPdfParser.scala (Apache PDFBox text extraction + fixed-column split) parsing a real INEMA bulletin (§4.3's captured Salvador bulletin, checked into local/src/test/resources/) into rows: point code, description, category. Multi-line description wrapping (confirmed real in §4.3's captured output) handled |
InemaPdfParserSpec against the real fixture PDF, point code, category and the multi-line-description case all asserted |
— |
| 2 | pdf-parser-inea | ✅ merged (PR #200): a sibling IneaPdfParser for INEA's real bulletin layout (§11's verified fetch: PRAIAS/LOCALIZAÇÃO/Ponto Coleta/CONAMA 274/2000), checked into a fixture the same way |
IneaPdfParserSpec against a real captured INEA fixture |
1 |
| 3 | coordinate-table-bahia | ✅ merged (PR #203): local/src/main/resources/sampling_points_ba.json, hand-curated: real coordinates for INEMA point codes near site/areas.json's salvador radius, each entry sourced (geocoded from the bulletin's own "Local da Coleta" description, per-entry note on how). The real data-entry task §11 flagged as the largest unknown, now scoped to just the points marola's configured area actually needs |
SamplingPointCoordinatesSpec: a point code present in the table resolves to its coordinate; one absent from it is dropped, not defaulted (same tolerant-parsing rule ImaScWaterQualityClient already follows) |
— |
| 4 | coordinate-table-rio | ✅ merged (PR #200): the same for INEA point codes near site/areas.json's rio radius, real data entry, same sourcing discipline |
Extends task 3's spec with INEA-shaped codes | — |
| 5 | inema-client | not started: InemaBaWaterQualityClient (fetches INEMA's live geraBoletim?idcampanha=N PDF, task 1's parser, task 3's coordinate table, drops unmatched points) wired into AppConfig.waterQualityClient's Auto case and WaterProvider enum (InemaBa case, MAROLA_WATER_QUALITY_PROVIDER override) |
InemaBaWaterQualityClientSpec, end-to-end from a fixture PDF to List[SamplingPoint], mirroring ImaScWaterQualityClientSpec's existing shape; a live check before merge: just run -- --brief --lat -12.9777 --lon -38.5016 shows a real water-quality line for at least one Salvador beach |
1, 3 |
| 6 | inea-client | not started: IneaRjWaterQualityClient, the extra step §11/§5 found INEMA doesn't need: fetch INEA's bulletin-listing page, find the newest PDF matching the rio area's zone(s), then task 2's parser + task 4's coordinates. Same AppConfig/WaterProvider wiring as task 5 |
IneaRjWaterQualityClientSpec including the listing-page discovery step (fixture the listing HTML, not just the PDF); live check: just run -- --brief --lat -22.9878 --lon -43.1913 shows a real water-quality line for at least one Rio beach |
2, 4 |
Sequencing notes¶
- Tasks 1/3 (Bahia) and 2/4 (Rio) are independent of each other: different institutes, different files, no shared code beyond whatever parser-sharing decision task 2 makes (flagged there as an implementation-time call, not pre-decided). They can be worked in parallel.
- Tasks 5 and 6 are the integration points and each depends on both halves (parser + coordinates) for their own institute, not parallel with their own dependencies, but 5 and 6 are parallel with each other once their prerequisites land.
- The
Riskrow this MIP's own header names, the coordinate table drifting as institutes add or retire points, with no self-updating feed to catch it, applies to tasks 3 and 4 specifically. Neither task's own tests can catch future drift (that's the nature of a hand-curated table); the mitigation is procedural (re-check whenjust benchmarkruns against ario/salvadororigin starts returning more "no data" than expected), not something to over-engineer into task 3/4's scope now.