MIP-0029: Positioning — marola as the ocean intelligence layer; "best hour to swim" as its first use case¶
| Status | Implemented — merged as PR #179 ("docs: implement MIP-0029 — marola as the ocean intelligence layer"), single-PR-sized per this MIP's own Effort row and the mip-tasks skill's "skip stacking for a single-PR change" rule |
| Author | Claude Sonnet 5, for M. Hoffmann (request of 2026-09-06: "rebrand marola to be the ocean layer intelligence, the best place to swim is just one of its own cases, consider this decision taking into consideration all planned mips"; refined the same day — "designed to be ocean intelligence layer, but acting like wave intelligence layer, but without being cocky … consider ALL MIP scopes") |
| Created | 2026-09-06 |
| Number note | 0029, not 0023 — 0020/0023/0024/0025 are already claimed by open drafts on remote branches (Instagram pipeline, waitlist-promotion, sea-model), and docs/4-Research-and-plans/ROADMAP.md §5 separately proposes 0023–0028 for unbuilt multi-agent work. Those two claims on 0023/0025 disagree and neither is this MIP's business to resolve — flagged here, revisited in §11 |
| Phase | 0 (docs and strings only; no pipeline, scoring, or infra change) — no earlier-phase prerequisite is missing |
| Related | Every MIP in docs/MIPs/README.md (0001–0022, surveyed in §4); docs/4-Research-and-plans/ROADMAP.md §5/§7's proposed 0023–0028; docs/4-Research-and-plans/FUTURE-WORK.md §1 (multi-activity generalization — the structural argument this MIP is a naming consequence of); .claude/skills/site-frontend/SKILL.md (the ≤12-word, no-adjective copy rule this MIP's wording must obey); open drafts docs/mip-0020-instagram-pipeline (handle @marola.swim) and docs/mip-0023-waitlist-promotion (domain strategy §5.1) — both name identity choices this MIP's wording must stay consistent with, flagged in §8/§11 |
| Effort | S — strings and docs only, no new module, no new dependency, no schema change. Touches ~9 files (§5) |
| Gain | user value (a five-second read of the map or the bot that doesn't undersell what the product already does — water quality, tides, sea lore, hazards, not swimming alone); community/outreach (a name that survives adding surf/dive/whale-watching without a second rebrand, relevant to MIP-0018/0020's promotion work) |
| Effort vs Gain | cheap win — no code risk, reversible in one PR, and every planned MIP in §4 already fits the frame without a scope change, so the cost of adopting it now is purely the string-and-doc diff |
| Depends on | Nothing blocks this at Phase 0. It should land before MIP-0018's exporter and MIP-0020's Instagram bio/first post start writing copy about the product (both cite README.md/app.js phrasing as their source text), so those don't have to be re-worded twice |
| Risk | A vaguer name loses the five-second clarity the map has today ("best hour to swim" tells a stranger exactly what to tap); if the tagline doesn't carry a concrete verb, the rebrand trades clarity for scope and nets nothing |
| Cost so far | — |
1. Summary¶
marola is already, in substance, more than a swim-time answer: MIP-0001 grounds it in bathing-water
quality and a sourced sea-lore corpus, MIP-0009/0016 put tides, wind, jellyfish and water-quality
marks on the map, MIP-0021/0022 add accessibility and a safety footer, and FUTURE-WORK.md §1
already argues the scoring engine should generalize past swimming to surf, dive, sailing, fishing,
whale watching. The product name doesn't change — marola stays marola everywhere (the README and
site <h1>, the repo, the bot, the CLI banner). What changes is the slogan paired with the name:
"marola — the ocean intelligence layer," the deterministic, sourced layer that knows the sea
near you (conditions, water quality, sea life, tides, hazards, lore) and answers questions about
it, with "what's the best hour tomorrow to swim nearby" named as its first case, not the whole
product. No scoring, safety text, or architecture changes; only the strings and docs that describe
the product.
2. Motivation — where the current, swim-only positioning lives¶
Every place the "best hour to swim" one-liner is the product description (not a data field, a test name, or a historical MIP title — those stay, they describe swimming, correctly, because swimming is the use case they built):
| Surface | File:line | Current text |
|---|---|---|
| README hero | README.md:4 |
"Local-first ocean intelligence for open-water swimmers: the best hour to swim tomorrow, official bathing-water quality…" |
| PHILOSOPHY.md, "Why marola" | PHILOSOPHY.md:23 |
"The question 'what is the best hour tomorrow to swim nearby?' is small enough to finish and hard enough to be honest about." |
| ARCHITECTURE.md §1, MVP hypothesis | docs/2-Building-marola/ARCHITECTURE.md:23 |
"MVP hypothesis: a Telegram message — 'what's the best hour tomorrow to swim nearby?' — gets back a ranked list…" |
| AGENTS.md, "What this repo is" | AGENTS.md:8 |
"marola — a Telegram assistant answering 'what's the best hour tomorrow to swim nearby?'" |
| Telegram bot description (BotFather) | docs/1-Using-marola/TELEGRAM-SETUP.md:22 |
example command list: `swim - best hour to swim nearby` |
Site <title> |
site/static/index.html:6 |
"marola — best hour to swim, every beach" |
| Site meta description | site/static/index.html:7 |
"Every beach around the area ranked by swim conditions for today and tomorrow: sea, wind, waves, water quality, tides, jellyfish. Computed once, no tracking." |
Site tagline (under the <h1>) |
site/static/index.html:16 |
"Best hour to swim at every beach: sea, wind, tide, water quality." |
CLI banner (--summarize/default run) |
cli/src/main/scala/marola/Main.scala:394 |
Console.printLine("marola :: best hour tomorrow to swim nearby (POC)") |
| CLI entry-point doc comment | cli/src/main/scala/marola/Main.scala:19 |
"POC entry point for 'what's the best hour tomorrow to swim nearby?'." |
Notably, README.md:4 already says "ocean intelligence": the phrase exists, but is immediately
narrowed to "for open-water swimmers" in the same sentence, and every other surface above still
frames the whole product as the swim question, not one case of a broader layer. PHILOSOPHY.md:23
and AGENTS.md:8 are the strongest examples: both use the swim question to justify the entire
architecture, when the actual justification (live data + a decision that can hurt someone if wrong
+ a sentence a person will read) applies to every use case §4 surveys, not swimming specifically.
3. User-visible change¶
3.1 The frame¶
Before: marola is a swim-time app that also happens to know about water quality and jellyfish.
After: the product name doesn't change — it's still marola, everywhere (README/site <h1>,
repo, bot, CLI). What changes is the slogan that now sits next to the name: "marola — the ocean
intelligence layer" — the sourced, deterministic layer that knows what the sea near you is doing
(conditions, water quality, sea life, tides, hazards, lore) and answers questions about it. "The
best hour tomorrow to swim nearby" is named as its first case — the one every current MIP was
built against — not a ceiling on what the layer can be asked, and not a rename.
3.2 On "OIA"¶
The maintainer's shorthand needs a fixed expansion or an explicit decision to stay internal.
Proposed: do not adopt "OIA" as a public name. "Ocean Intelligence Agent" overstates what
marola does today — no autonomous agent exists yet (only one implicit,
hardcoded two-agent pipeline), and "Assistant" duplicates AGENTS.md:8's existing
"Telegram assistant." Keep "ocean intelligence layer" as the prose phrase (matches the
layer/pluggable-integration language ARCHITECTURE.md already uses for
LlmClient/VisionClient/SightingStore); "OIA" stays an internal planning shorthand only,
never in README/site/bot copy. An initialism in front of a five-second read works against the
site-frontend skill's own rule (.claude/skills/site-frontend/SKILL.md:74, "nouns from the
data," not an acronym).
3.3 The wording, before/after¶
No marketing adjectives anywhere below ("smart", "AI-powered", "seamless", "revolutionary") — the
repo's tone, per PHILOSOPHY.md and the site-frontend skill, is the number and its source.
Name — slogan pair (the name never changes; this string is the pairing used wherever a title
line appears): "marola — the ocean intelligence layer." Used for the README title line (the
bold subtitle directly under the <h1>🌊 marola</h1> wordmark, which stays as-is) and the site's
<title> tag (the browser-tab text, distinct from the on-page <h1>marola</h1> wordmark, which
also stays as-is).
One-line description (≤ 12 words, concrete nouns, site-frontend skill rule) — sits under the name—slogan pair, e.g. as the on-page tagline:
- Before (
site/static/index.html:16): "Best hour to swim at every beach: sea, wind, tide, water quality." (11 words, already compliant, but swim-only) - After: "The ocean near you: conditions, water quality, sea life, tides, hazards." (11 words, unchanged from the earlier draft of this MIP)
README title line + hero paragraph (README.md:1-7):
- Before:
<h1>🌊 marola</h1>then "When LLMs meet the ocean. Local-first ocean intelligence for open-water swimmers: the best hour to swim tomorrow, official bathing-water quality per sampling point, tides, jellyfish and whale odds, and a grounded 'ask the ocean' — all on your own machine with a free model (Scala 3 / Kyo / Ollama), sourced or clearly labelled, never invented." - After:
<h1>🌊 marola</h1>(unchanged) then "marola — the ocean intelligence layer. The ocean near you: conditions, official bathing-water quality per sampling point, tides, jellyfish and whale odds, and a grounded 'ask the ocean' — first case, the best hour tomorrow to swim, all on your own machine with a free model (Scala 3 / Kyo / Ollama), sourced or clearly labelled, never invented."
Site <title> and on-page tagline (site/static/index.html:6,16): <title> changes from
"marola — best hour to swim, every beach" to "marola — the ocean intelligence layer" (the
name—slogan pair, browser tab only). The on-page <h1>marola</h1> wordmark is unchanged; the
<p class="tagline"> under it becomes the one-line description above: "The ocean near you:
conditions, water quality, sea life, tides, hazards." Meta description (index.html:7) drops
"swim conditions" for "conditions" and keeps the rest verbatim (jellyfish, tides, water quality
already read as more than swimming), and gains a closing clause naming the first case: "…Computed
once, no tracking. First case: the best hour tomorrow to swim."
Bot description (docs/1-Using-marola/TELEGRAM-SETUP.md:22's example, cosmetic, set via BotFather):
- Before:
swim - best hour to swim nearby - After:
swim - best hour to swim nearbystays as the command description (the command is literally about swimming — no reason to rename it), but the bot's own/setdescriptiontext (not yet written, since Phase 1 isn't built) should read: "marola — the ocean intelligence layer: conditions, water quality, tides, sea life. First case,/swim, the best hour tomorrow." — documented indocs/1-Using-marola/TELEGRAM-SETUP.mdas guidance for whoever registers the description at Phase 1, since the bot isn't live to set it today.
CLI top banner (cli/src/main/scala/marola/Main.scala:394) — the name stays first, unchanged:
- Before:
"marola :: best hour tomorrow to swim nearby (POC)" - After:
"marola :: the ocean intelligence layer (POC) — first case: best hour tomorrow to swim nearby"
PHILOSOPHY.md's "Why marola" opening (PHILOSOPHY.md:23):
- Before: "The question 'what is the best hour tomorrow to swim nearby?' is small enough to finish and hard enough to be honest about."
- After: "marola — the ocean intelligence layer: the question it answers first, 'what is the best hour tomorrow to swim nearby?', is small enough to finish and hard enough to be honest about — and the same layer (live data, a decision that can hurt someone if wrong, a sentence a person will read) is what any other question about the sea near you needs too."
AGENTS.md "What this repo is" (AGENTS.md:8):
- Before: "marola — a Telegram assistant answering 'what's the best hour tomorrow to swim nearby?' — real nearby beach discovery…"
- After: "marola — the ocean intelligence layer for a stretch of coast, reachable as a Telegram assistant: real nearby beach discovery… Its first case is 'what's the best hour tomorrow to swim nearby?'…" (rest of the paragraph unchanged — it already lists water quality, jellyfish/whale, corpus, local-first, unchanged by this MIP).
4. MIP-by-MIP fit — every planned or drafted MIP as part of one ocean-intelligence project¶
Read against the new frame, not a swim app with extras bolted on. "Scope/wording change?" is almost always no: the point of the rebrand is that it costs nothing to adopt, because the work these MIPs describe was never swim-specific; it was described in swim terms because that was the product's only stated use case.
| MIP | What it adds to the ocean layer | Use case(s) served | Scope/wording change? |
|---|---|---|---|
| 0001 water quality + sea lore | Bathing-water quality per point; sourced sea-lore corpus (any topic) | swim, sea life/lore | No, already layer-shaped |
| 0002 Telegram bot, Phase 1 | The addressable surface the layer is reached through | all | No, only its description text (§3) |
| 0003 fast replies / caching | Answers in under 3s regardless of question | all | No |
| 0004 daily digest / subscriptions | Push habit; digest content is swim-shaped today | swim now; any case once parameterized | Wording: "conditions," not "swim conditions" |
| 0005 the map, static site | Visual face of the layer, one marker per beach | all (renders swim score today) | Wording only, §3.3's title/tagline/meta |
| 0006 live look, user cameras | Ground-truth visual conditions, activity-independent | swim, dive, surf | No |
| 0007 time-series foundation models | Better forecasts of the same live series | all | No |
| 0008 Docker images | Ships the layer, any backend | all | No |
| 0009 wave markers + hover aspects | Wind/whale/jellyfish/temperature per point | swim, sea life, any activity | No |
| 0010 MLflow experiment tracking | Ledger for runs across whatever the layer is asked | all | No |
| 0011 Claude Code best practices | Dev-tooling only | none (infra) | No |
| 0012 llm4s / DSPy deprecation | Synthesis step's implementation, not its scope | all | No |
| 0013 OpenCode tryout | Dev-tooling only | none (infra) | No |
| 0014 marola book | Outreach artifact explaining the project | outreach | Wording: lead with the layer, not "swim app" |
| 0015 Interação swim matching | Who else is swimming here, deliberately swim-only | swim only | No, correctly scoped, no need to generalize |
| 0016 water-quality map markers | Water quality placed correctly on the map | swim, any water-touching activity | No |
| 0017 agentic tooling survey | Dev-tooling ideas | none (infra) | No |
| 0018 self-documentation / exporter | Publishes about the project (LinkedIn/blog/Reddit) | outreach | Wording: posts lead with the layer, swim as example |
| 0019 arXiv ocean-forecasting survey | Better forecast/jellyfish-prediction techniques | all (activity-agnostic) | No |
| 0020 Instagram bot (draft) | Promotion; bio/captions say "best hour…to swim" today, handle @marola.swim |
outreach | Wording: bio should follow §3.3; handle is that MIP's call, flagged §8 |
| 0021 beach accessibility | Parking/toilets/showers/lifeguard from OSM | swim, any beach-based activity | No |
| 0022 safety answer footer | Lifeguard/193/SAMU grounding, activity-agnostic | all | No, §6 says this MIP doesn't touch safety text |
| 0023 waitlist draft, wait-list/promotion/maintenance | Site copy ("soon, in the bot"), domain strategy | outreach | Wording: copy tracks §3.3; domain should read coastal, not swim-only, per §8 |
| 0025 sea-model draft, marola-sea-1.0 fine-tune | Domain-tuned local model for the whole synthesis step (tool-calls, safety adherence) | all | No, "marola-sea" already reads layer-shaped |
| ROADMAP §5's planned 0023–0028 (hazard/escalation agent, actor topology, A2A, eval harness) | Multi-agent build-out: escalation agent on the same live series, addressable roles, shared state, governed identity | safety (activity-agnostic hazard), all (eval/identity underlie every use case) | No, architecture work; this frame makes "a third agent with a genuinely different responsibility" legible as a layer capability |
FUTURE-WORK.md §1, surf, dive, sail, whale-watch, fish |
The naming consequence this MIP responds to: one ActivityScoring per activity, same data, same layer |
surf, dive, sail, whale-watch, fishing | Not drafted; a second use case needs an ActivityScoring (§1.2), activity-aware Recommender/BestHour, per-activity card copy, a bot "what are you doing?" turn (ROADMAP.md K1); none built; this MIP only clears the naming precondition |
5. Design — what changes in code, and what doesn't¶
Only strings and docs change. No trait, module, schema, or scoring function is touched. Files this MIP's implementation PR would edit:
README.md:3-7: hero paragraph (§3.3)PHILOSOPHY.md:23: "Why marola" opening sentence (§3.3)docs/2-Building-marola/ARCHITECTURE.md:23-24: reframe "MVP hypothesis" as the layer's first use case, keep the Telegram-message example verbatim (it's still accurate)AGENTS.md:8: "What this repo is" (§3.3); this is also whereCLAUDE.md's@AGENTS.mdimport means the change propagates automatically to Claude Code's view, no separateCLAUDE.mdeditdocs/1-Using-marola/TELEGRAM-SETUP.md:22: add the proposed/setdescriptiontext as guidance (§3.3); leave the/swimcommand example as-issite/static/index.html:6,7,16: title, meta description, tagline (§3.3)cli/src/main/scala/marola/Main.scala:19,394: doc comment and banner string (§3.3)docs/MIPs/README.md: this MIP's index row (this same PR, per themipskill step 5)
Docs index update: docs/index.md (the doc-index file AGENTS.md points to) gets no new row.
It already indexes docs/MIPs/ as a directory, not per-MIP; nothing there names the swim
question specifically enough to need a change. docs/4-Research-and-plans/ROADMAP.md is left untouched by this MIP
(it's a snapshot dated 2026-09-06, and editing history there is out of scope) but its own "How this
file stays true" rule means whoever files the MIP-0029 implementation PR should add one line
noting the frame is adopted, per that file's own maintenance instruction.
6. Scoring / safety impact¶
None, and this MIP explicitly must not touch:
scoring/Swimability.scala's thresholds or any ranking logic.- Safety text: the MIP-0022 footer (lifeguard/193/SAMU), the whale/jellyfish heuristic disclaimers
in
docs/2-Building-marola/ARCHITECTURE.md§8. - The local-first promise: "entirely locally with a free Ollama model, no cloud account needed"
(
AGENTS.md's clause, unchanged) and every per-integration design inARCHITECTURE.md§5. - The "no cookies, no tracking" line, verified live at
site/static/app.js:11("no analytics, no cookies") andsite/static/app.js:280("No cookies, no tracking…soon, in the Telegram bot"), stays verbatim; §3.3 only changes the tagline above it, not this footer sentence. - MIP-0005's own constraints (plain files, no build step, one accent colour, the score-colour
tokens): this MIP changes copy inside
index.html, not layout, markers, or the schemasite/board.schema.jsondepends on.
7. Verification plan¶
No test gate applies to a docs/strings-only change (just quality runs scalafmt/scalafix/ruff/
actionlint/hadolint — none of it checks prose). Verification here is manual, and was already done
while writing this MIP:
- Every file:line cited in §2 and §5 was read directly (
grep -nagainst the actual file in this worktree, not memory) before being quoted; see the Appendix for the raw commands. - The proposed one-liner and tagline (§3.3) were word-counted by hand: 11 words each, under the
site-frontend skill's ≤12-word rule (
.claude/skills/site-frontend/SKILL.md:74). - No instance of "smart", "AI-powered", "seamless", "revolutionary", "world-class", or an exclamation mark appears in any proposed replacement string above; checked by re-reading §3.3.
- Once implemented:
node --check site/static/app.js(unaffected — no JS changes), the site-frontend skill'ssite_check.jsharness re-run to confirm the tagline renders (its DOM assertions target markers/list rendering, not copy content, so this is a smoke check not a gate).
8. Risks, limitations, and honest caveats¶
- A vaguer name loses five-second clarity. "Best hour to swim, every beach" tells a stranger exactly what to tap; "the ocean near you" needs one more glance to land on an action. §3.3 keeps a concrete verb ("swim tomorrow," in the README hero and CLI line) to hedge this, bounded to the site tagline and title, which stay short by design.
- Identity choices in open drafts must agree with this name. The waitlist draft's §5.1 domain
strategy (
marola.app.bretc.) doesn't reference "ocean"/"layer": fine, a domain needn't restate the tagline, but its site-copy quote should be re-checked against §3.3 when finalized. The Instagram draft's handle@marola.swimis a stronger conflict: a handle saying "swim" next to a site saying "the ocean near you" is a real cross-surface inconsistency. This MIP doesn't resolve it; flagged for whoever finalizes that MIP, repeated in §11. - Nothing here needed external verification. No web search or pricing check was made or needed: every citation in §2/§5 is a local file read.
9. Alternatives considered¶
- Keep "swim" as the brand. Zero cost, maximum clarity, but caps the story as MIP-0009/0016/ 0021's non-swim work keeps growing past what "swim app" implies.
- "Beach intelligence." Anchors to a place rather than the sea itself (which matters offshore, for divers and sailors who never touch the beach): narrower than "ocean," not chosen.
- "Sea assistant." Duplicates
AGENTS.md:8's existing "assistant" noun and undersells the deterministic-layer framingPHILOSOPHY.mdargues for; not chosen. - A separate product name per use case ("marola-swim," "marola-surf"). Multiplies the branding
work
FUTURE-WORK.md§1 explicitly designed against (oneActivityScoringobject per activity, no other code touched); rejected as the opposite of that design goal. - Do nothing. Cheapest, and not wrong today since no second use case is built, but every MIP in §4 already assumes the layer framing works without a rename, so the only real cost of waiting is a second copy pass once MIP-0018/0020 have written more text against the swim-only wording.
11. Open questions¶
- Does the Portuguese wording change? "Marola" (a small wave) already carries the intended
modesty: big scope by design (asks about anything the sea is doing), small and concrete in what
it says (one beach, one hour, one number with its source). No Portuguese string in the repo was
found that needs a corresponding English-side change (
grep -rn "marola" *.md docs/*.mdreturns only the product name itself, never translated prose). Open question is whether a future Portuguese-language bot reply (docs/4-Research-and-plans/ROADMAP.mdK10, i18n) should state the wordplay explicitly or leave it implicit, as English copy does today. - Do the Telegram handle / domain choices follow this rebrand?
@marola.swim(MIP-0020) and themarola.app.brfamily (MIP-0023 draft §5.1) were proposed before this positioning existed. A human decision is needed on whether to hold those MIPs' identity sections for this MIP to merge first, or let them proceed and reconcile wording only (not the handle/domain itself, which has its own registration-cost and availability constraints unrelated to naming taste). - Does the map's
<h1>stay "marola"? Resolved by the maintainer, 2026-09-06: yes, the product name stays marola everywhere (README/site<h1>, repo, bot, CLI banner); "the ocean intelligence layer" is a slogan paired with the name (in the site's<title>and the README's title line), not a replacement for the wordmark. §3.3 reflects this; the on-page<h1>andsite/static/index.html:14's wave icon are unchanged, only<title>(browser tab) and the<p class="tagline">text change. - Who resolves the 0023/0025 numbering collision noted in the metadata table? Not this MIP's job, but the collision (ROADMAP's proposed multi-agent numbers vs. the waitlist/sea-model drafts that already claimed them) should be settled before either set of drafts merges, to avoid two MIPs sharing a number.
Appendix¶
Raw verification commands run against this worktree (docs/mip-0029-ocean-layer-positioning
branch, based on origin/main) while drafting this MIP:
grep -n "best hour to swim tomorrow" README.md → README.md:4
grep -n "what is the best hour tomorrow to swim nearby" PHILOSOPHY.md → PHILOSOPHY.md:23
grep -n "MVP hypothesis" docs/2-Building-marola/ARCHITECTURE.md → docs/2-Building-marola/ARCHITECTURE.md:23
grep -n "^\*\*marola\*\*" AGENTS.md → AGENTS.md:8
grep -n "swim - best hour to swim nearby" docs/1-Using-marola/TELEGRAM-SETUP.md → docs/1-Using-marola/TELEGRAM-SETUP.md:22
grep -n "best hour to swim, every beach|tagline|meta name=\"description\"" site/static/index.html
→ index.html:6 (title), 7 (meta description), 16 (tagline)
grep -n "best hour tomorrow to swim nearby (POC)|POC entry point" cli/src/main/scala/marola/Main.scala
→ Main.scala:19 (doc comment), 394 (banner)
grep -n "Nothing leaves the browser|no cookies|no analytics|soon, in the Telegram" site/static/app.js
→ app.js:11 ("no analytics, no cookies"), app.js:280 ("No cookies, no tracking...")
Remote drafts read for §4/§8 (not merged, fetched via git fetch origin then git show):
origin/docs/mip-0020-instagram-pipeline:docs/MIPs/MIP-0020-instagram-bot-account.md,
origin/docs/mip-0023-waitlist-promotion:docs/MIPs/MIP-0023-waitlist-promotion-and-maintenance.md,
origin/docs/mip-0025-marola-sea-model:docs/MIPs/MIP-0025-marola-sea-domain-model.md.
docs/4-Research-and-plans/ROADMAP.md and docs/MIPs/README.md read from origin/main for §4's ROADMAP row and the
full 0001–0022 index.