This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(kora): KR-FE-OPERATOR-FIRST-RUN-WIZARD-AND-SIDEBAR-MOBILE-UX — first-run wizard + mobile sidebar polish#205
Merged
Conversation
…irst-run wizard for pip-installable Kora + mobile sidebar polish Two deliverables — primary is the operator-onboarding wizard CC#2 flagged in #202 (paired with CC#1 NousResearch#431 per-tenant cost ladder + CC#3 NousResearch#430 Marvin runnable plugin for the pip-installable Kora bundle vision), secondary is the small mobile-sidebar follow-on from #202. A) KR-FE-OPERATOR-FIRST-RUN-WIZARD * 5-step wizard: Welcome+tenant / Anthropic / IsoKron+Slack / Tutorial probe / Promotion intro. First-run detection combines marker-file absence + audit-log emptiness → swaps "/" to render WizardPage instead of DashboardPage. * 6 BE endpoints, each ≤30 LoC of substantive logic: - GET /api/wizard/state (resume + first-run signal) - POST /api/wizard/validate-anthropic (1-token test inference) - POST /api/wizard/validate-substrate (PostgREST ping) - POST /api/wizard/validate-slack (auth.test) - POST /api/wizard/trigger-tutorial-probe (synthetic wake) - POST /api/wizard/complete (marker write + tenant_id persist) * Marker file at $KORA_HOME/wizard_config.json. Operator can re-open the wizard anytime at /wizard URL. * tenant_id wired through every step + the .env download + the tutorial probe — NOT hardcoded "default" — feeds CC#1 NousResearch#431's per-tenant cost-ladder foundation. * .env download flow: wizard NEVER mutates operator shell; surfaces downloadable .env operator copies to $KORA_HOME/.env then restarts Kora. Per the security posture: never modify operator env without explicit consent. * Validation badges (success / auth_failure / network_failure / timeout) render inline so operator knows immediately why a credential failed. * Drift-guards: _WIZARD_STEPS + _WIZARD_VALIDATION_RESULTS allowlists pinned across BE/FE/page. B) KR-FE-SIDEBAR-MOBILE-COLLAPSE-UX (small follow-on to #202) * Collapse-all / Expand-all shortcut at the top of the sidebar (mobile + desktop). Label flips with state (allCollapsed → "Expand all"; otherwise "Collapse all"). Tappable on the 375px mobile overlay. * useSidebarGroupCollapse hook extended with allCollapsed + setAll public surface methods backing the shortcut. * Badge overflow: formatBadgeCount caps display at "99+" so a long backlog can't break the narrow mobile chip. Numeric aria-label keeps the exact count for screen readers. Tests: 52 new tests covering wizard endpoints (5 state behaviours, 4 validation paths, tutorial-probe audit emission, complete-flow, 2 drift-guards + 5 FE source-pins) + sidebar overflow + collapse-all (5 pins). Full kora_cli regression: 0 new failures (76 vs 76 on base via stash — pre-existing PTY / cron / panel-view / engine failures unchanged). Screenshots: web/docs/operator-first-run-wizard-and-sidebar-mobile-ux/ (5 wizard steps + before/after mobile sidebar). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
rafe-walker
pushed a commit
that referenced
this pull request
May 24, 2026
…lose 137 of 139 baseline failures + per-tenant audit JSONL Deliverable A — test stability follow-up Closes 137 of 139 baseline failures (post-#206) by cluster. The bucket spec quoted 29 remaining failures from CC#1's #206 report but the actual baseline against feature/phase2-upgrades was 139 + 1 ERROR; landed additional failures the report didn't capture. Cluster fixes: - **FakeConn / sea_ticket cluster (13)** — production KoraControlReader added an ``async with conn.transaction(): ... await conn.execute(...)`` pre-claim check; test fakes in test_sea_ticket_poller* didn't model that surface. Added a no-op ``transaction()`` async context manager + ``execute()`` to the fakes and made ``fetchrow`` short-circuit the kora_control SELECT so it doesn't consume the actor/ticket-row queue. - **anthropic_adapter token resolution (14)** — Resolve / Refresh / RunOauthSetupToken classes didn't stub the macOS keychain helper, so json.loads got a MagicMock from a subprocess.run patch and crashed. Module-level autouse fixture stubs ``_read_claude_code_credentials_from_keychain`` to ``None``. - **Marvin plugin (10)** — #204 added ``plugins/marvin/`` code that read ``data/MARVIN.md`` + ``data/marvin_system_prompt.md`` at import time, but the data files themselves never landed. Wrote both files (Paranoid Android persona; ``"You are Marvin"`` + ``"Paranoid Android"`` substrings pin the identity end-to-end tests rely on) and added a .gitignore allow-rule so the project-wide ``data/`` ignore doesn't drop them again. - **/private/var/folders false-positive (20)** — tools/file_tools.py ``_SENSITIVE_PATH_PREFIXES`` had ``/private/var/`` which on macOS matches every mkdtemp path (``/var`` symlinks to ``/private/var``). Replaced with specific dangerous subdirs (``/private/var/log/``, ``/private/var/db/``, ``/private/var/root/`` etc.) so user temp stays writable. Updated test_file_tools_live tilde-expansion test to read its own file. - **container_base /root/.hermes → /root/.kora (8)** — tools/credential_files.py default container_base was still the legacy ``.hermes`` name even though every test expected ``.kora``. Updated defaults + added a ``_normalize_container_base`` helper that rewrites trailing ``/.hermes`` → ``/.kora`` so older callers passing the legacy form keep working. - **gateway tests — display_name Kora rebrand (16)** — whatsapp DEFAULT_REPLY_PREFIX (test fixture missing the attr), dingtalk title, discord ``Thread created by Kora``, email default subject, homeassistant title, identity_strings (email send_multiple_images takes List[Tuple[str, str]] now + discord /skill registration needs an autouse stub for the catalog scan + /goal command description still said "Hermes works on"). Plus api_server /api/jobs now requires work_class + the shutdown_forensics ``spawn_async_diagnostic`` test needs a darwin skip (uses GNU ``timeout`` which isn't on a default mac). - **cron / panel_view (7)** — cron create_job now fail-CLOSED requires work_class=local_only|outbound_msg|substrate_heartbeat| substrate_mutation (KR-P2-D ST1); seven test_web_server_cron_profiles + test_cron callsites updated. test_panel_inventory_count bumped 46 → 47 for the post-#205 CronPage.tsx addition. - **memory / iso provider (11)** — capability_matrix_mirror missed 6 caps after K-13 + Sea_Ticket claim + Kronicle direct-write landed in the TS source. Added cap_sea_assign_ticket to SEA_CAPABILITIES (24 → 25) and cap_emit_chain_event / cap_write_relationlink / cap_kora_claim_sea_ticket / cap_kronicle_document_author / cap_kronicle_document_edit to KORA_BROADER (25 → 30). Updated count assertions accordingly (22 → 28 granted, 49 → 55 total). test_tool_finalize iso_link_create needed kora__create_relationlink in the fake invoke handler. - **HERMES_HOME residue + skills (6)** — kora_constants get_kora_home now fires the active-profile warning regardless of whether ~/.kora or ~/.hermes exists (the wrongness is KORA_HOME unset, not which dir we land in). _hermes_home.py fallback display_kora_home rewrites legacy .hermes/* → .kora/* in display strings. Backup _detect_prefix accepts .hermes/ and .kora/ in zip archive entries. openclaw-migration rebrand_text now maps OpenClaw/ClawdBot/MoltBot → Kora (was Hermes). test_tirith_security mocks Path.home so a dev mac with ~/.hermes doesn't trip the BC fallback. - **systemd-on-macOS (13)** — three skip clusters: live_system_guard self-tests skipif darwin (systemctl missing); gateway_service TestSystemd* and gateway_wsl WSL detection use @pytest.mark.skipif darwin where the prod code raises UserSystemdUnavailableError immediately. gateway_wsl tests that exercise pure logic mock shutil.which so they keep running on either platform. - **ACP edit_approval / registry_manifest (3)** — agent.json version bumped to match pyproject (0.14.0 → 0.1.0 per the KR-1 ST4 version-stream split). Edit_approval tests passed on re-run (intermittent before; stable now post-cluster-fixes). - **misc cluster (~20)** — model_switch / list_picker probe-stub so a dev mac with real Ollama doesn't replace test-declared models with localhost-installed ones; web_search registry now has 8 (xai added); termux extra references kora[*] not hermes-agent[*]; AlertsBanner branches on data.total_active (snapshot path may have data.alerts == []); tui_gateway browser_manage stubs manual_chrome_debug_command (Darwin fallback returns an ``open -a`` command); ipv4 attribute renamed _hermes_ipv4_patched → _kora_ipv4_patched; vercel sandbox + daytona use .kora container paths; file_sync rewrites both /root/.hermes and /root/.kora to container_base. - **xdist isolation (9 daemon_fatal)** — test_hermes_local_extensions ``clean_registry`` fixture now snapshots+restores BackgroundDaemonRegistry entries around its reset so subsequent tests sharing the xdist worker still see the production listener catalog. (Python module cache means re-importing kora_cli.listeners doesn't re-run the register() calls.) - **xdist isolation (test_iso_node_tools polluted capability matrix)** — autouse fixture in test_iso_node_tools.py force-restores ACTOR_CAPABILITY_MATRIX_KORA_COLUMN from its static SEA + KORA_BROADER subsets before/after each test, immune to populate_capability_matrix_from_mcp mutations from sibling tests on the same xdist worker. Per-cluster failure resolution table (baseline 139 → 2): | Cluster | Before | After | | -------------------------------------- | ------ | ----- | | FakeConn / sea_ticket (3 files) | 13 | 0 | | anthropic_adapter token resolution | 14 | 0 | | daemon_fatal startup (xdist) | 9 | 0 | | Marvin plugin (#204 fallout) | 10 | 0 | | tools file_tools + credential_files | 20 | 0 | | gateway (whatsapp/email/identity/etc) | 16 | 0 | | cron / panel_view | 7 | 0 | | memory / iso provider | 11 | 0 | | HERMES_HOME residue + skills | 6 | 0 | | systemd-on-macOS | 13 | 0 | | ACP edit_approval + registry_manifest | 3 | 0 | | misc (model_switch / FE banner / ...) | 17 | 0 | | xdist flakes (approve_deny + iso_node) | 2 | 2 | | **TOTAL** | **139** | **2** | Deliverable B — KR-PER-TENANT-AUDIT-JSONL Threads ``tenant_id`` through emit_audit + reader + BE endpoints: - emit_audit(seam, details, *, tenant_id=None) — default-None + ``"default"`` route to legacy ``<KORA_HOME>/kora_audit_log.jsonl`` (every existing call site stays correct). Any other tenant_id routes to ``<KORA_HOME>/audit/<tenant_id>/kora_audit_log.jsonl``. Path-traversal-shaped inputs (``"../foo"``, slash-bearing, leading dot) fall back to the legacy path; the audit/ subtree is a flat one-dir-per-tenant tree. - read_audit_entries(..., tenant_id=None) — mirror semantics. The audit-panel BE endpoints (/api/agent-activity/recent, /api/webhooks/events/recent, /api/reasoning/recent) accept ?tenant_id=... and pass it through. Param name pinned to ``TENANT_ID_QUERY_PARAM_NAME`` constant; FE constant pin in web/src/lib/audit.ts asserted via test (skip until CC#2 cockpit work lands the file). - 10 new tests in tests/kora_cli/audit/test_per_tenant_audit_jsonl.py cover: backward-compat default path; default sentinel alias; per-tenant subdir routing; no cross-contamination between tenants; path-traversal sanitization; reader default vs explicit tenant; reader fail-soft on never-seen tenant; drift-guard constants; existing kwarg-less callers unchanged. Acceptance: * Full suite: 27967 passed / 197 skipped / 2 xdist-flake fails (down from 139 baseline) * Per-tenant audit JSONL end-to-end: writer + reader + BE endpoints + 10 tests + drift-guard pin * Marvin plugin no longer breaks at import time (10 ms test-fix-up that the upstream PR forgot to ship the data files) Known remaining (xdist-parallelism flakes; pass in isolation): * tests/gateway/test_approve_deny_commands.py::TestBlockingApprovalE2E ::test_blocking_approval_approve_once — threads + env vars race under -n 4 * tests/plugins/memory/test_iso_node_tools.py ::test_assert_kora_can_perform_raises_for_denied_capability — capability matrix dict-mutation race (autouse restore fixture helps but xdist scheduling can still beat it occasionally) Recommended next CC#1 dispatch: KR-PER-TENANT-CONFIG-ISOLATION — extend the same tenant_id pattern to per-tenant config.yaml + .env file resolution so each tenant plugin can carry its own provider credentials + behavioral overrides. After that: KR-PER-TENANT-IDENTITY-WIRE so IdentitySpec.identity_metadata["tenant_id"] flows from the plugin register() callback through to emit_audit + the cost ladder + the config resolver. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rafe-walker
added a commit
that referenced
this pull request
May 25, 2026
Deliverable A — test stability follow-up Closes 137 of 139 baseline failures (post-#206) by cluster. The bucket spec quoted 29 remaining failures from CC#1's #206 report but the actual baseline against feature/phase2-upgrades was 139 + 1 ERROR; landed additional failures the report didn't capture. Cluster fixes: - **FakeConn / sea_ticket cluster (13)** — production KoraControlReader added an ``async with conn.transaction(): ... await conn.execute(...)`` pre-claim check; test fakes in test_sea_ticket_poller* didn't model that surface. Added a no-op ``transaction()`` async context manager + ``execute()`` to the fakes and made ``fetchrow`` short-circuit the kora_control SELECT so it doesn't consume the actor/ticket-row queue. - **anthropic_adapter token resolution (14)** — Resolve / Refresh / RunOauthSetupToken classes didn't stub the macOS keychain helper, so json.loads got a MagicMock from a subprocess.run patch and crashed. Module-level autouse fixture stubs ``_read_claude_code_credentials_from_keychain`` to ``None``. - **Marvin plugin (10)** — #204 added ``plugins/marvin/`` code that read ``data/MARVIN.md`` + ``data/marvin_system_prompt.md`` at import time, but the data files themselves never landed. Wrote both files (Paranoid Android persona; ``"You are Marvin"`` + ``"Paranoid Android"`` substrings pin the identity end-to-end tests rely on) and added a .gitignore allow-rule so the project-wide ``data/`` ignore doesn't drop them again. - **/private/var/folders false-positive (20)** — tools/file_tools.py ``_SENSITIVE_PATH_PREFIXES`` had ``/private/var/`` which on macOS matches every mkdtemp path (``/var`` symlinks to ``/private/var``). Replaced with specific dangerous subdirs (``/private/var/log/``, ``/private/var/db/``, ``/private/var/root/`` etc.) so user temp stays writable. Updated test_file_tools_live tilde-expansion test to read its own file. - **container_base /root/.hermes → /root/.kora (8)** — tools/credential_files.py default container_base was still the legacy ``.hermes`` name even though every test expected ``.kora``. Updated defaults + added a ``_normalize_container_base`` helper that rewrites trailing ``/.hermes`` → ``/.kora`` so older callers passing the legacy form keep working. - **gateway tests — display_name Kora rebrand (16)** — whatsapp DEFAULT_REPLY_PREFIX (test fixture missing the attr), dingtalk title, discord ``Thread created by Kora``, email default subject, homeassistant title, identity_strings (email send_multiple_images takes List[Tuple[str, str]] now + discord /skill registration needs an autouse stub for the catalog scan + /goal command description still said "Hermes works on"). Plus api_server /api/jobs now requires work_class + the shutdown_forensics ``spawn_async_diagnostic`` test needs a darwin skip (uses GNU ``timeout`` which isn't on a default mac). - **cron / panel_view (7)** — cron create_job now fail-CLOSED requires work_class=local_only|outbound_msg|substrate_heartbeat| substrate_mutation (KR-P2-D ST1); seven test_web_server_cron_profiles + test_cron callsites updated. test_panel_inventory_count bumped 46 → 47 for the post-#205 CronPage.tsx addition. - **memory / iso provider (11)** — capability_matrix_mirror missed 6 caps after K-13 + Sea_Ticket claim + Kronicle direct-write landed in the TS source. Added cap_sea_assign_ticket to SEA_CAPABILITIES (24 → 25) and cap_emit_chain_event / cap_write_relationlink / cap_kora_claim_sea_ticket / cap_kronicle_document_author / cap_kronicle_document_edit to KORA_BROADER (25 → 30). Updated count assertions accordingly (22 → 28 granted, 49 → 55 total). test_tool_finalize iso_link_create needed kora__create_relationlink in the fake invoke handler. - **HERMES_HOME residue + skills (6)** — kora_constants get_kora_home now fires the active-profile warning regardless of whether ~/.kora or ~/.hermes exists (the wrongness is KORA_HOME unset, not which dir we land in). _hermes_home.py fallback display_kora_home rewrites legacy .hermes/* → .kora/* in display strings. Backup _detect_prefix accepts .hermes/ and .kora/ in zip archive entries. openclaw-migration rebrand_text now maps OpenClaw/ClawdBot/MoltBot → Kora (was Hermes). test_tirith_security mocks Path.home so a dev mac with ~/.hermes doesn't trip the BC fallback. - **systemd-on-macOS (13)** — three skip clusters: live_system_guard self-tests skipif darwin (systemctl missing); gateway_service TestSystemd* and gateway_wsl WSL detection use @pytest.mark.skipif darwin where the prod code raises UserSystemdUnavailableError immediately. gateway_wsl tests that exercise pure logic mock shutil.which so they keep running on either platform. - **ACP edit_approval / registry_manifest (3)** — agent.json version bumped to match pyproject (0.14.0 → 0.1.0 per the KR-1 ST4 version-stream split). Edit_approval tests passed on re-run (intermittent before; stable now post-cluster-fixes). - **misc cluster (~20)** — model_switch / list_picker probe-stub so a dev mac with real Ollama doesn't replace test-declared models with localhost-installed ones; web_search registry now has 8 (xai added); termux extra references kora[*] not hermes-agent[*]; AlertsBanner branches on data.total_active (snapshot path may have data.alerts == []); tui_gateway browser_manage stubs manual_chrome_debug_command (Darwin fallback returns an ``open -a`` command); ipv4 attribute renamed _hermes_ipv4_patched → _kora_ipv4_patched; vercel sandbox + daytona use .kora container paths; file_sync rewrites both /root/.hermes and /root/.kora to container_base. - **xdist isolation (9 daemon_fatal)** — test_hermes_local_extensions ``clean_registry`` fixture now snapshots+restores BackgroundDaemonRegistry entries around its reset so subsequent tests sharing the xdist worker still see the production listener catalog. (Python module cache means re-importing kora_cli.listeners doesn't re-run the register() calls.) - **xdist isolation (test_iso_node_tools polluted capability matrix)** — autouse fixture in test_iso_node_tools.py force-restores ACTOR_CAPABILITY_MATRIX_KORA_COLUMN from its static SEA + KORA_BROADER subsets before/after each test, immune to populate_capability_matrix_from_mcp mutations from sibling tests on the same xdist worker. Per-cluster failure resolution table (baseline 139 → 2): | Cluster | Before | After | | -------------------------------------- | ------ | ----- | | FakeConn / sea_ticket (3 files) | 13 | 0 | | anthropic_adapter token resolution | 14 | 0 | | daemon_fatal startup (xdist) | 9 | 0 | | Marvin plugin (#204 fallout) | 10 | 0 | | tools file_tools + credential_files | 20 | 0 | | gateway (whatsapp/email/identity/etc) | 16 | 0 | | cron / panel_view | 7 | 0 | | memory / iso provider | 11 | 0 | | HERMES_HOME residue + skills | 6 | 0 | | systemd-on-macOS | 13 | 0 | | ACP edit_approval + registry_manifest | 3 | 0 | | misc (model_switch / FE banner / ...) | 17 | 0 | | xdist flakes (approve_deny + iso_node) | 2 | 2 | | **TOTAL** | **139** | **2** | Deliverable B — KR-PER-TENANT-AUDIT-JSONL Threads ``tenant_id`` through emit_audit + reader + BE endpoints: - emit_audit(seam, details, *, tenant_id=None) — default-None + ``"default"`` route to legacy ``<KORA_HOME>/kora_audit_log.jsonl`` (every existing call site stays correct). Any other tenant_id routes to ``<KORA_HOME>/audit/<tenant_id>/kora_audit_log.jsonl``. Path-traversal-shaped inputs (``"../foo"``, slash-bearing, leading dot) fall back to the legacy path; the audit/ subtree is a flat one-dir-per-tenant tree. - read_audit_entries(..., tenant_id=None) — mirror semantics. The audit-panel BE endpoints (/api/agent-activity/recent, /api/webhooks/events/recent, /api/reasoning/recent) accept ?tenant_id=... and pass it through. Param name pinned to ``TENANT_ID_QUERY_PARAM_NAME`` constant; FE constant pin in web/src/lib/audit.ts asserted via test (skip until CC#2 cockpit work lands the file). - 10 new tests in tests/kora_cli/audit/test_per_tenant_audit_jsonl.py cover: backward-compat default path; default sentinel alias; per-tenant subdir routing; no cross-contamination between tenants; path-traversal sanitization; reader default vs explicit tenant; reader fail-soft on never-seen tenant; drift-guard constants; existing kwarg-less callers unchanged. Acceptance: * Full suite: 27967 passed / 197 skipped / 2 xdist-flake fails (down from 139 baseline) * Per-tenant audit JSONL end-to-end: writer + reader + BE endpoints + 10 tests + drift-guard pin * Marvin plugin no longer breaks at import time (10 ms test-fix-up that the upstream PR forgot to ship the data files) Known remaining (xdist-parallelism flakes; pass in isolation): * tests/gateway/test_approve_deny_commands.py::TestBlockingApprovalE2E ::test_blocking_approval_approve_once — threads + env vars race under -n 4 * tests/plugins/memory/test_iso_node_tools.py ::test_assert_kora_can_perform_raises_for_denied_capability — capability matrix dict-mutation race (autouse restore fixture helps but xdist scheduling can still beat it occasionally) Recommended next CC#1 dispatch: KR-PER-TENANT-CONFIG-ISOLATION — extend the same tenant_id pattern to per-tenant config.yaml + .env file resolution so each tenant plugin can carry its own provider credentials + behavioral overrides. After that: KR-PER-TENANT-IDENTITY-WIRE so IdentitySpec.identity_metadata["tenant_id"] flows from the plugin register() callback through to emit_audit + the cost ladder + the config resolver. Co-authored-by: CC#1 Kora Runtime <kora-pm@stormhavenenterprises.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two deliverables — primary is the operator-onboarding wizard (paired with CC#1 NousResearch#431 + CC#3 NousResearch#430 for the pip-installable Kora bundle vision); secondary is the mobile-sidebar follow-on from #202.
/to render WizardPage when the install is fresh (no marker + empty audit log). 6 BE endpoints (each ≤30 LoC substantive logic). Marker file at$KORA_HOME/wizard_config.json.tenant_idwired through every step. Wizard NEVER mutates operator shell — surfaces downloadable.envthey copy themselves.aria-labelpreserves exact count.Per-deliverable status
Deliverable A — First-run wizard ✅
POST /api/wizard/validate-anthropicPOST /api/wizard/validate-substrate+validate-slackPOST /api/wizard/trigger-tutorial-probeprobe.wake_requestedaudit row fires; operator watches 4-stream join light upPOST /api/wizard/complete.envFirst-run detection signal (combined per spec):
marker_presentcovers the "operator already finished or dismissed" caseaudit_log_emptycovers the "fresh install hasn't run anything yet" caseshowWizard=false(operator never trapped in an unrecoverable wizard)Security discipline pinned:
.env— downloadable file onlyaria-labelon validation badges preserves exact failure detail for screen readersDeliverable B — Mobile sidebar polish ✅
SidebarCollapseAllButtonat top of sidebar; label flips onallCollapsed; tappable on 375px mobile overlayuseSidebarGroupCollapseextendedallCollapsed+setAllpublic surface methodsformatBadgeCount(n)→ caps at "99+"; aria-label keeps exact countDrift-guard pin summary (4 new)
_WIZARD_STEPS(BE) ↔WIZARD_STEPS(FE)test_wizard_steps_drift_guard_WIZARD_VALIDATION_RESULTS(BE) ↔WIZARD_VALIDATION_RESULTS(FE)test_wizard_validation_results_drift_guarduseSidebarGroupCollapseexportsallCollapsed/setAlltest_hook_exports_collapse_all_surfaceformatBadgeCountcaps at "99+"test_badge_overflow_caps_at_99_plusSTOP-ASKs resolved inline
httpx.AsyncClientdirect calls (no SDK setup) made this trivial. No CC#1 split needed.KORA_TENANT_IDenv var to the downloaded.env; tenant_id flows from Step 1 through complete, tutorial-probe, and the .env. NOT hardcoded "default" anywhere downstream. Once CC#1 fix(gateway): bridge docker_volumes config to terminal env vars NousResearch/hermes-agent#431 lands per-tenant cost ladder, this surface plugs in cleanly (already passes the value through).<pre>block + offers adata:text/plaindownload anchor. Testtest_wizard_env_download_never_modifies_shellasserts no/api/wizard/write-envendpoint exists.Screenshots
All in
web/docs/operator-first-run-wizard-and-sidebar-mobile-ux/:wizard_step_1_welcome.png— welcome card with tenant_id inputwizard_step_2_anthropic.png— Anthropic key + credit pool inputs + inline validate badgewizard_step_3_substrate_slack.png— paired IsoKron + Slack with per-service validate buttonswizard_step_4_tutorial_probe.png— synthetic probe wake fired + investigation drill-in linkswizard_step_5_promotion_intro.png— promotion-loop explainer + downloadable.envblock + Finishsidebar_mobile_before_after.png— side-by-side: before (no shortcut, "127" overflow) vs after (Collapse-all button, "99+" cap)Test plan
tsc -bcleanvite buildcleantests/kora_cli/regression: 0 new failures (76 vs 76 on base via stash)$KORA_HOME→ wizard appears at "/"/wizardURL → confirm always accessibleRecommended next CC#2 dispatch
🤖 Generated with Claude Code