feat(home): drop /home connectors block — onboarding covers it (#305)

The dedicated `<details data-section="connectors">` section on /home
duplicated content that the install hero's Step 4 clipboard payload
already inlines. Both surfaces sourced the same prompt strings from
`app/web/connector_prompts.py` (home tiles via `<code id="*-prompt">`,
setup script via `app/web/setup_instructions.py::_connectors_block`),
so users walking the install script visited each connector inline and
then had no reason to scroll back up.

Removed the full block (3 tiles + summary + section-label). Lead
paragraph in the install hero now mentions the connector families
briefly so the benefit is visible before kick-off:

  "... your team's curated data, plugins, third-party tools (Asana,
   Google Workspace, Atlassian), and shared knowledge ... the install
   script also connects your tools for you, so there's no extra page
   to visit."

The "Email admin" mailto CTA, previously gated inside the GWS tile
when admin_email was set + GWS unconfigured, moves implicitly to the
install script's GWS step (Claude prompts the user when the OAuth
gating wall lands). Tests updated:

- test_connectors_section_removed_from_home (renamed from
  test_connectors_render_flat_when_onboarded_by_default) — asserts
  `class="connector-tiles"` and `data-section="connectors"` are absent
  in BOTH onboarded states, and that the lead paragraph still mentions
  the three connector families so the benefit isn't lost.
- test_home_renders_connector_prompts_from_shared_module — DROPPED.
  Was a parity check between the home tiles and the setup script's
  connector_prompts.py source. One surface now → no drift risk → test
  redundant. Replaced with an inline comment pointing future readers
  at where the strings flow (setup_instructions.py::_connectors_block).
- test_home_no_longer_shows_email_admin_button (renamed from
  test_home_shows_email_admin_button_when_admin_email_set_and_gws_unconfigured)
  — asserts the mailto CTA is gone from /home regardless of
  admin_email / GWS-configured state; documents the path-move.

CSS for `.connector-tile*` left in place as dead bytes — small
footprint, no behavior, easy follow-up if/when someone audits.
This commit is contained in:
Vojtech 2026-05-14 20:31:24 +04:00 committed by GitHub
parent 3e19caa975
commit d63f1473ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 176 deletions

View file

@ -1453,7 +1453,7 @@
<div class="eyebrow">Welcome, {{ display_name }} — let's get you set up</div>
<h1>Connect Claude Code on your machine to your team's data</h1>
<p class="lead">
{{ instance_brand }} gives <strong>Claude Code</strong> on your computer access to your team's <strong>curated data, plugins, and shared knowledge</strong> — so you can ask questions and get answers in plain language, right from your terminal. This page walks you through the <strong>one-time setup (~10 minutes)</strong>. Everything it installs lives in your home folder (<code style="background: rgba(255,255,255,0.12); padding: 1px 6px; border-radius: 4px; font-family: var(--hp-font-mono); font-size: 12.5px;">~/{{ workspace_dir }}</code>) and can be removed in one command.
{{ instance_brand }} gives <strong>Claude Code</strong> on your computer access to your team's <strong>curated data, plugins, third-party tools (Asana, Google Workspace, Atlassian), and shared knowledge</strong> — so you can ask questions and get answers in plain language, right from your terminal. This page walks you through the <strong>one-time setup (~10 minutes)</strong>; the install script also connects your tools for you, so there's no extra page to visit. Everything it installs lives in your home folder (<code style="background: rgba(255,255,255,0.12); padding: 1px 6px; border-radius: 4px; font-family: var(--hp-font-mono); font-size: 12.5px;">~/{{ workspace_dir }}</code>) and can be removed in one command.
</p>
<div class="install-block">
@ -1662,99 +1662,17 @@ Set-Location "$HOME\{{ workspace_dir }}"</span>
<p class="home-usage-foot">For the deepest integration, create every project under <code>~/{{ workspace_dir }}/Projects/</code> — existing or new. The bundled plugin keeps each project in sync with the central catalog automatically, and the session-analysis loop is scoped to that root.</p>
</section>
<details class="setup-collapsible" data-section="connectors" open>
<summary>
<span class="ico" aria-hidden="true">&#x1F517;</span>
<span class="ttl">Connect your tools <small>(Asana / Google Workspace / Atlassian)</small></span>
<span class="chev" aria-hidden="true">&rsaquo;</span>
</summary>
<div class="section-label section-label-flat">Once {{ instance_brand }} is installed — connect your tools</div>
<div class="connector-tiles">
<div class="connector-tile">
<span class="ico">&#x2705;</span>
{# P0-3 — title row + time-badge + post-copy hint. The hint
container is rendered hidden; the JS reveals it after the
copy succeeds, then auto-hides after 8 s. #}
<div class="ttl-row">
<span class="ttl">Asana</span>
<span class="time-badge">~5 min · self-serve</span>
</div>
<div class="desc">Read tasks and projects, comment, create updates — Claude works alongside your project boards without leaving the terminal.</div>
<div class="connector-actions">
<button class="connector-copy" data-copy-target="asana-prompt" data-connector="Asana">Copy prompt</button>
<div class="copy-next-hint" data-hint-for="Asana">
<span>&#x2705; Copied. Now paste into Claude Code — run <code>claude</code> in your terminal, then paste &amp; press Enter.</span>
</div>
<details class="connector-preview">
<summary>Show prompt</summary>
{# Asana prompt body sourced from app/web/connector_prompts.py
(`asana_prompt()`). Same string the main setup script
inlines in step 9, so the two surfaces stay in lockstep. #}
<div class="card-mini-cmd"><code id="asana-prompt">{{ connector_prompts.asana }}</code></div>
</details>
</div>
</div>
<div class="connector-tile">
<span class="ico">&#x1F4DA;</span>
{# P0-3 + P1-8 — Google Workspace tile. Time badge warns when
the operator hasn't provisioned a shared OAuth app (forces
the user to set up GCP themselves, which is a ~20-min
clickops detour). The gating-note + email-admin button
appear in that same un-configured branch so the user has
a way out before copying. #}
<div class="ttl-row">
<span class="ttl">Google Workspace</span>
{% if gws_oauth.configured %}
<span class="time-badge">~5 min · self-serve</span>
{% else %}
<span class="time-badge is-warn">~20 min · admin help likely</span>
{% endif %}
</div>
<div class="desc">Drive, Calendar, Gmail, Docs, Sheets, Chat — Claude reads and acts across your work account via the official <code>gws</code> CLI.</div>
{% if not gws_oauth.configured %}
<div class="gating-note">
<strong>Heads up:</strong> your {{ instance_brand }} admin hasn't provisioned a shared Google Cloud OAuth app yet, so this connector needs GCP project setup (creating an OAuth client, enabling APIs). It's fastest to ask your admin first — the button below pre-fills the email.
</div>
{% endif %}
<div class="connector-actions">
<button class="connector-copy" data-copy-target="gws-prompt" data-connector="Google Workspace">Copy prompt</button>
{% if not gws_oauth.configured and instance_admin_email %}
<a class="email-admin" href="mailto:{{ instance_admin_email }}?subject={{ instance_brand|urlencode }}%20%E2%80%94%20Google%20Workspace%20OAuth%20setup&amp;body=Hi%20%E2%80%94%20I'd%20like%20to%20connect%20Google%20Workspace%20in%20{{ instance_brand|urlencode }}%20but%20it%20looks%20like%20a%20shared%20OAuth%20app%20isn't%20provisioned%20yet.%20Could%20you%20set%20it%20up%20for%20our%20instance%3F%20Thanks!">
&#x2709;&#xFE0F; Email admin
</a>
{% endif %}
<div class="copy-next-hint" data-hint-for="Google Workspace">
<span>&#x2705; Copied. Now paste into Claude Code — run <code>claude</code> in your terminal, then paste &amp; press Enter.</span>
</div>
<details class="connector-preview">
<summary>Show prompt</summary>
<div class="card-mini-cmd"><code id="gws-prompt">{{ connector_prompts.gws }}</code></div>
</details>
</div>
</div>
<div class="connector-tile">
<span class="ico">&#x1F39F;&#xFE0F;</span>
<div class="ttl-row">
<span class="ttl">Atlassian (Jira / Confluence)</span>
<span class="time-badge">~7 min · self-serve</span>
</div>
<div class="desc">Read and write Jira issues, search Confluence pages — Claude pulls ticket context and posts updates without leaving the workspace.</div>
<div class="connector-actions">
<button class="connector-copy" data-copy-target="jira-prompt" data-connector="Atlassian">Copy prompt</button>
<div class="copy-next-hint" data-hint-for="Atlassian">
<span>&#x2705; Copied. Now paste into Claude Code — run <code>claude</code> in your terminal, then paste &amp; press Enter.</span>
</div>
<details class="connector-preview">
<summary>Show prompt</summary>
<div class="card-mini-cmd"><code id="jira-prompt">{{ connector_prompts.atlassian }}</code></div>
</details>
</div>
</div>
</div>
</details>
{# Connectors `<details data-section="connectors">` block removed —
the install-hero's Step 4 clipboard payload (rendered via
`_claude_setup_instructions.jinja` inside the "Or paste manually"
fallback) already inlines the same Asana / GWS / Atlassian
prompts from app/web/connector_prompts.py via
app/web/setup_instructions.py::_connectors_block. Showing them
a second time as standalone cards duplicated UX without adding
reach — the install script visits them all in sequence. Brief
mention in the install-hero lead paragraph above covers the
benefits ("third-party tools (Asana, Google Workspace,
Atlassian)"); deep ops live in /setup-advanced. #}
<div class="section-label">Want to look around first?</div>
<p class="look-around-lead">You don't need {{ instance_brand }} installed locally to browse what's available. Anything you bookmark or subscribe to will be there waiting after you set {{ instance_brand }} up.</p>

View file

@ -109,20 +109,20 @@ def test_home_onboarded_user_sees_nav_hub(fresh_db):
assert "Step 4 — install" not in body
def test_connectors_render_flat_when_onboarded_by_default(fresh_db):
"""Connect-your-tools section must NOT auto-collapse on the
server-side `users.onboarded=TRUE` flip. It renders flat (in <details
open>) by default; only an explicit user click on the in-hero
"Minimize setup view" toggle (persisted in localStorage, not server)
activates the collapsed bar layout.
def test_connectors_section_removed_from_home(fresh_db):
"""The dedicated `<details data-section="connectors">` block was
dropped from `/home` the install-hero's Step 4 clipboard payload
(rendered via `_claude_setup_instructions.jinja` inside the manual
fallback) already inlines the same Asana / GWS / Atlassian prompts
from `app/web/connector_prompts.py` via
`app/web/setup_instructions.py::_connectors_block`. Showing them
twice on the same page was duplicate UX. The lead paragraph in the
install-hero now mentions the connectors briefly so users still see
the benefit before they hit the install.
Auto-mode used to be a peer `setup-collapsible` section
(`data-section="step3"`) outside the install-hero. It moved into the
install-hero as Step 2 of the install flow (so users enable it
BEFORE Step 3's ~20-command install runs), and the standalone
outside-hero copy was dropped to avoid duplicating reference
content. Onboarded users no longer see the auto-mode block at all
consistent with Step 1 + Step 3 also hiding post-onboarding."""
Co-asserts the auto-mode block removal that this test originally
pinned onboarded users still see neither the connectors block
nor the legacy auto-mode peer section."""
from src.db import get_system_db, close_system_db
conn = get_system_db()
@ -136,22 +136,37 @@ def test_connectors_render_flat_when_onboarded_by_default(fresh_db):
resp = c.get("/home", cookies={"access_token": sess})
assert resp.status_code == 200
body = resp.text
# Auto-mode no longer renders for onboarded users — both the
# in-hero install-block and the legacy outside-hero `<details>`
# reference card are gated `{% if not onboarded %}` / removed.
# Auto-mode peer section still gone (legacy guard, not regressed).
assert 'class="automode-card"' not in body
assert 'data-section="step3"' not in body
assert "Step 2 — turn on auto-mode" not in body
# Connect-your-tools section is still flat-open by default.
assert 'class="connector-tiles"' in body
assert 'class="setup-collapsible" data-section="connectors" open' in body
# Dedicated connectors block is gone from /home in BOTH states.
assert 'class="connector-tiles"' not in body
assert 'data-section="connectors"' not in body
# Server-rendered HTML never carries the data-setup-minimized
# attribute on the .home-mock root — that's a client-side
# localStorage decision applied via JS on load. The token still
# appears in inline CSS selectors and the JS body, which is fine.
# localStorage decision applied via JS on load.
assert '<div class="home-mock" data-setup-minimized' not in body
assert 'class="home-mock"\n' in body or '<div class="home-mock">' in body
# Not-onboarded path: same — the section disappears regardless of
# state. Lead-paragraph still surfaces the connector names so users
# know the benefit exists before they kick off the install.
conn = get_system_db()
try:
_, sess2 = _make_user_and_session(
conn, email="not-onboarded@example.com", onboarded=False
)
finally:
conn.close()
close_system_db()
body2 = _client().get("/home", cookies={"access_token": sess2}).text
assert 'class="connector-tiles"' not in body2
assert 'data-section="connectors"' not in body2
# Lead-paragraph mentions the three connector families so the
# benefit isn't lost when the dedicated section disappears.
assert "Asana, Google Workspace, Atlassian" in body2
def test_minimize_toggle_no_longer_rendered(fresh_db):
"""The "Minimize setup view" toggle used to live inside the
@ -237,12 +252,15 @@ def test_home_hides_email_admin_button_when_admin_email_unset(fresh_db, monkeypa
assert "mailto:?" not in body # specifically, no broken empty mailto
def test_home_shows_email_admin_button_when_admin_email_set_and_gws_unconfigured(
fresh_db, monkeypatch,
):
"""When admin_email is set AND gws_oauth is unconfigured, the mailto
link renders. (Both conditions required see template guard
``{% if not gws_oauth.configured and instance_admin_email %}``.)"""
def test_home_no_longer_shows_email_admin_button(fresh_db, monkeypatch):
"""The Email-admin mailto CTA used to live inside the /home GWS
connector tile. With the dedicated `<details data-section="connectors">`
block removed (see test_connectors_section_removed_from_home above),
the button has no rendering site even when admin_email is set + GWS
is unconfigured. The escalation path lives inside the install
script's GWS step now — Claude prompts the user with the admin
email when the connector setup hits an OAuth gating wall, so the
affordance moves to the surface where it's actually useful."""
monkeypatch.setenv("AGNES_INSTANCE_ADMIN_EMAIL", "ops@example.com")
monkeypatch.delenv("AGNES_GWS_CLIENT_ID", raising=False)
monkeypatch.delenv("AGNES_GWS_CLIENT_SECRET", raising=False)
@ -254,8 +272,8 @@ def test_home_shows_email_admin_button_when_admin_email_set_and_gws_unconfigured
conn.close()
close_system_db()
body = _client().get("/home", cookies={"access_token": sess}).text
assert "Email admin" in body
assert "mailto:ops@example.com" in body
assert "Email admin" not in body
assert 'mailto:ops@example.com' not in body
def test_home_hides_email_admin_button_when_gws_configured(fresh_db, monkeypatch):
@ -276,59 +294,17 @@ def test_home_hides_email_admin_button_when_gws_configured(fresh_db, monkeypatch
assert "Email admin" not in body
def test_home_renders_connector_prompts_from_shared_module(fresh_db):
"""Single source of truth check: the prompt text the /home tiles
paste must equal the strings ``app/web/connector_prompts.py`` returns.
The same strings are also inlined into the setup script's step 9, so
if they ever drift the two surfaces would tell users to do different
things this test catches that early."""
import html as _html
import re
from src.db import get_system_db, close_system_db
from app.web.connector_prompts import (
asana_prompt, gws_prompt, atlassian_prompt,
)
from app.instance_config import (
get_gws_oauth_credentials, get_instance_admin_email,
)
conn = get_system_db()
try:
_, sess = _make_user_and_session(conn)
finally:
conn.close()
close_system_db()
c = _client()
body = c.get("/home", cookies={"access_token": sess}).text
# Resolve the same gws_oauth dict the route uses so the parity check
# exercises whichever branch (configured / manual) is active in the
# current test environment.
gws = get_gws_oauth_credentials()
expected_gws = gws_prompt(
gws_oauth_configured=bool(gws.get("configured")),
gws_client_id=str(gws.get("client_id") or ""),
gws_client_secret=str(gws.get("client_secret") or ""),
gws_project_id=str(gws.get("project_id") or ""),
oauthlib_insecure_transport=str(gws.get("oauthlib_insecure_transport") or "1"),
instance_admin_email=get_instance_admin_email(),
)
for slug, expected in (
("asana", asana_prompt()),
("gws", expected_gws),
("jira", atlassian_prompt()),
):
m = re.search(rf'<code id="{slug}-prompt">(.*?)</code>', body, re.DOTALL)
assert m, f"{slug}-prompt block missing from /home"
actual = _html.unescape(m.group(1))
assert actual == expected, (
f"{slug}-prompt body diverged from connector_prompts module — "
f"the home tile and setup script will paste different text. "
f"len(home)={len(actual)} len(module)={len(expected)}"
)
# `test_home_renders_connector_prompts_from_shared_module` was dropped here
# alongside the removal of the /home `<details data-section="connectors">`
# block. The test pinned source-of-truth parity between the home tile
# `<code id="*-prompt">` blocks and `app/web/connector_prompts.py`. With the
# tiles gone, the only surface left for those strings is the install-hero's
# Step 4 clipboard payload (rendered via `_claude_setup_instructions.jinja`
# from `setup_instructions_lines`, which is built in
# `app/web/setup_instructions.py::_connectors_block` calling the same
# `connector_prompts.py` functions). One surface, no drift risk → the
# parity test is redundant. If a second surface ever re-renders these
# prompts, restore a parity test scoped to that new consumer.
# ── Getting Started + Overview + Usage modes (PR #289 home additions) ────