diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb80d5b..7517ac1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,35 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
## [Unreleased]
+## [0.54.7] — 2026-05-13
+
+### Added
+
+- `instance.overview` yaml field (env override
+ `AGNES_INSTANCE_OVERVIEW`) — operator-authored HTML body rendered in
+ the new Overview section on `/home`. HTML in, HTML out via the same
+ `| safe` filter as `news_intro`. Empty default hides the section,
+ keeping the OSS vendor-neutral.
+- `/home` Getting Started card — dismissible, two clickable rows
+ linking to `/setup` (install) and `/setup-advanced` (deeper
+ reference). Per-device dismiss via localStorage key
+ `agnes_home_gs_dismissed`. Generic `.home-card-close[data-dismiss-key]`
+ + `` pattern — drop-in for any future dismissible card.
+- `/home` Usage modes section — three OSS-shipped tiles (Terminal /
+ VS Code / Claude Desktop · claude.ai) explaining each surface and
+ linking to the relevant `/setup-advanced` anchors.
+- `setup_advanced.html` `#claude-app` section anchored by the Usage
+ modes tile — covers the marketplace registration paths (git
+ smart-HTTP + ZIP fallback) and when to prefer the terminal anyway.
+
+### Changed
+
+- `/home` legacy `.advanced-pointer` row (the "Going deeper —
+ Advanced setup" link that sat above the news section) removed —
+ the same link now lives in the new Getting Started card. Supporting
+ `.advanced-pointer` CSS stays in place as dead style to keep the
+ diff focused.
+
## [0.54.6] — 2026-05-13
### Changed
diff --git a/app/instance_config.py b/app/instance_config.py
index 15bb39d..84aa9b8 100644
--- a/app/instance_config.py
+++ b/app/instance_config.py
@@ -295,6 +295,22 @@ def get_instance_logo_svg() -> str:
return (raw or "").strip()
+def get_instance_overview() -> str:
+ """Operator-authored Overview body rendered on ``/home``. Markdown is
+ NOT auto-converted — operators paste HTML (matches the existing
+ ``news_intro`` ``| safe`` filter). Empty default = section hidden,
+ keeping the OSS vendor-neutral when an instance ships without
+ operator-specific framing.
+
+ Resolution: ``AGNES_INSTANCE_OVERVIEW`` env > ``instance.overview``
+ YAML > ``""``. Mirrors :func:`get_instance_logo_svg`.
+ """
+ raw = os.environ.get("AGNES_INSTANCE_OVERVIEW")
+ if raw is None:
+ raw = get_value("instance", "overview", default="")
+ return (raw or "").strip()
+
+
def get_workspace_dir_name() -> str:
"""Filesystem-safe folder name for the analyst's local workspace
(``~/``). Defaults to :func:`get_instance_brand`
diff --git a/app/web/router.py b/app/web/router.py
index 5cfa975..b31a11d 100644
--- a/app/web/router.py
+++ b/app/web/router.py
@@ -25,7 +25,7 @@ from app.instance_config import (
get_gws_oauth_credentials, get_home_automode_visibility,
get_instance_admin_email, get_atlassian_base_url,
get_instance_brand, get_workspace_dir_name,
- get_instance_logo_svg,
+ get_instance_logo_svg, get_instance_overview,
)
from app.web.connector_prompts import all_connector_prompts
from src.repositories.sync_state import SyncStateRepository
@@ -345,6 +345,7 @@ def _build_context(
INSTANCE_SUBTITLE = get_instance_subtitle()
INSTANCE_COPYRIGHT = ""
LOGO_SVG = get_instance_logo_svg()
+ INSTANCE_OVERVIEW = get_instance_overview()
TELEGRAM_BOT_USERNAME = os.environ.get("TELEGRAM_BOT_USERNAME", "")
SSH_ALIAS = "data-analyst"
SERVER_HOST = os.environ.get("SERVER_HOST", "")
diff --git a/app/web/templates/home_not_onboarded.html b/app/web/templates/home_not_onboarded.html
index 5be2c0b..654bb07 100644
--- a/app/web/templates/home_not_onboarded.html
+++ b/app/web/templates/home_not_onboarded.html
@@ -101,6 +101,155 @@
}
.home-mock .install-hero .lead strong { font-weight: 600; }
+/* ── Getting Started + Overview + Usage modes (added in PR #289) ───
+ Three new content cards rendered between the install-hero and the
+ connector tiles. Getting Started + Usage modes ship in OSS;
+ Overview body comes from instance.overview yaml (operator-owned),
+ hidden when unset. Generic dismiss pattern: any element with
+ `
")
+ from src.db import get_system_db, close_system_db
+
+ conn = get_system_db()
+ try:
+ _, sess = _make_user_and_session(conn)
+ finally:
+ conn.close()
+ close_system_db()
+ body = _client().get("/home", cookies={"access_token": sess}).text
+ assert '' in body
+ assert "OVERVIEW_TEST_MARKER" in body
+ # Overview must NOT carry a dismiss key — content stays
+ # reachable on every visit so users can re-read it.
+ assert 'data-dismiss-key="agnes_home_overview_dismissed"' not in body
+
+
+def test_overview_section_hidden_when_yaml_empty(fresh_db, monkeypatch):
+ """Default empty `instance.overview` (no env override) hides the
+ section entirely so the OSS ships without a stray empty
+ Overview placeholder."""
+ monkeypatch.delenv("AGNES_INSTANCE_OVERVIEW", raising=False)
+ from src.db import get_system_db, close_system_db
+
+ conn = get_system_db()
+ try:
+ _, sess = _make_user_and_session(conn)
+ finally:
+ conn.close()
+ close_system_db()
+ body = _client().get("/home", cookies={"access_token": sess}).text
+ assert '' not in body
diff --git a/uv.lock b/uv.lock
index 0e155f5..299f26f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -24,7 +24,7 @@ wheels = [
[[package]]
name = "agnes-the-ai-analyst"
-version = "0.54.6"
+version = "0.54.7"
source = { editable = "." }
dependencies = [
{ name = "a2wsgi" },