UX review P0: header markings, actionable dashboard, /admin hub, notebook phases#265
Conversation
There was a problem hiding this comment.
IcebergAutoReview
Verdict: request_changes
Two P0 behaviors are incorrect despite green CI: notebook deep links can hide their targets, and several admin status pills report broken integrations as healthy.
Findings
notebookTabs()always initializes tophase: 'collect'and ignoreslocation.hash(src/iceberg/static/js/tags.js:412). Consequently direct links and existing post-action redirects such as/notebooks/{id}#diamondsand#achleave the requested target inside an Alpine-hidden, cloaked Analyze phase. Initialize the phase/section from the hash (and cover redirect/deep-link behavior with a browser-level test).- The admin hub does not consistently distinguish configured integrations from broken ones. For example, the AI tile is always green whenever
backend != 'none'(effective_config.py:278), even whenai_settings.validate_selection()rejects it for a missing model, API key, region, or base URL and runtime resolution disables it. Similarly, SSO can appear green with an empty client secret, and HTTP audit can claim off-box forwarding without an endpoint/token. These should showNOT CONFIGURED/warning using the same readiness validation as their configuration/runtime paths, with transition tests beyond MISP and webhook.
Validation
git diff --check origin/m4/effective-config...HEADpassed.- Inspected the complete one-commit diff and merge ancestry against
origin/m4/effective-config. - Attempted the focused new hub/dashboard/editor/notebook tests with system pytest; collection could not start because FastAPI is unavailable in the review environment. The first
uv runattempt also could not create.venvbecause the checkout is read-only. No files were changed.
Residual risks / optional notes
- Current tests assert that phase anchors render, but do not exercise Alpine hydration, hash-based entry, or existing redirect destinations.
Automated review by Codex b51574171591 using IcebergAutoReview.
9c9b3c6 to
97fc3b1
Compare
…, /admin hub, notebook phases Implements the four P0 items from the Claude Design UX handoff (design_handoff_iceberg_ux/README.md) as template + CSS + Alpine changes on the existing stack. No route removed, no asset pin touched, no new design tokens. Editor · markings promoted to the header. Intel level, TLP and analytic confidence move out of the Citations dock footer into always-visible chips, so a product's markings are one source of truth and are legible while writing. Each chip lays its real <select form="reportform"> transparently over itself: a click opens the native dropdown, so it stays keyboard/screen-reader navigable and renders correctly before Alpine hydrates. The component only mirrors the chosen option's own text back into the chip, so there is no label map to drift from the enums. Dashboard · actionable. Every KPI card links to the work behind it, and a "Needs you now" queue leads: the review queue (for anyone who can review) ahead of the viewer's own drafts, capped at five. Both halves are existing lifecycle state surfaced as actions — no new model. Notebook creation is demoted to a collapsed <details> so content leads. Admin · Settings & integrations hub at GET /admin. One tile per configurable subsystem with a live status pill, grouped Outbound integrations / Governance. Status comes from effective_config.admin_hub_tiles(), which reads the same settings singletons the config pages edit, so the hub cannot disagree with the page it links to. The pill separates OFF (opt-in integration disabled) from NOT CONFIGURED (enabled but missing an endpoint or its env-only secret) — the state an operator most needs flagged. Secrets are consulted as set/not-set only. Notebook · phase tabs. The nine stacked sections become Collect / Analyze / Produce / Trace, one phase at a time, with the collection strip re-cut as the tab bar. Nothing is gated: the tabs are real #section anchors, Collect is the server-rendered default, and a <noscript> rule cancels x-cloak so a browser without Alpine still gets every section. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
IcebergAutoReview
Verdict: request_changes
The AI readiness fix is correct, but both prior blockers remain partially or wholly unresolved, and the branch currently conflicts with main.
Findings
- The branch cannot merge cleanly into current main.
git merge-treereports a conflict insrc/iceberg/services/effective_config.py, where both branches changed the AI tile logic. Rebase and retain the hub plus runtime-aligned AI validation. - Notebook deep links remain broken at
src/iceberg/static/js/tags.js:412:notebookTabs()still initializes to Collect/Sources without readinglocation.hash. Existing#diamondsand#achreturn links therefore target content hidden after Alpine hydration. - Admin readiness remains inaccurate at
src/iceberg/services/effective_config.py:368. SSO is green when a provider has a client ID but no required client secret, becauseenabled_providers()retains empty-secret configurations. The audit tile is also green for an HTTP method with no endpoint, whilesiem._emit_http()silently returns without forwarding. Add runtime-equivalent readiness checks and transition tests.
Validation
git diff b515741715912107dc7a71685c06225193882420..HEADinspected; only effective-config and focused tests changed.git diff --check b515741715912107dc7a71685c06225193882420..HEADpassed.git merge-tree $(git merge-base main HEAD) main HEADdetected a conflict ineffective_config.py.- Focused pytest could not run:
uvcould not create.venvon the read-only checkout, and the system Python lacks pytest. - Static inspection verified the new AI hub/config tiles call
ai_settings.validate_selection()and correctly warn when runtime resolution fails closed.
Residual risks / optional notes
- Notebook tests still verify server-rendered sections only; they do not exercise Alpine hydration or hash-based entry.
Automated review by Codex 16a8b45b4278 using IcebergAutoReview.
…flect readiness (review)
Two review findings on the P0 change set, both real.
**Notebook deep links landed on a cloaked phase.** `notebookTabs` always started
on Collect and ignored `location.hash`, but eleven routes redirect to
`/notebooks/{id}#<section>` — and `#diamonds` / `#ach` live in Analyze. Deleting
a Diamond model returned you to a page that appeared to have lost the whole
section. The component now resolves the hash to its owning phase, re-scrolls
once that phase is visible (the browser's own scroll happened while the target
was display:none), and re-applies on `hashchange`.
The section → phase map is no longer duplicated in JS: the template builds the
tab bar and the JSON island from one `phases` list, so adding a section cannot
leave the two out of step. Tests assert the map covers every rendered section id
and that the delete redirect's anchor resolves to Analyze.
**Hub pills called broken integrations healthy.** The AI tile was fixed in #264;
the same class of defect applied to two more:
- SSO went green on a client id alone. A provider with no env client secret
cannot complete the authorization-code flow — it looks enabled and fails at
login, which is precisely when a green pill sends you looking in the wrong
place. Now NOT CONFIGURED, naming the provider.
- Audit claimed off-box forwarding whenever a non-stdout method was selected,
including an HTTP sink with no endpoint. That is worse than "local only",
because it looks solved. Now NOT CONFIGURED with the reason.
Transition tests for both, in the style of the existing MISP/webhook ones.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
IcebergAutoReview
Verdict: approve
All prior blockers are resolved. The branch merges cleanly with current main; notebook deep links now activate the owning phase; and SSO/audit readiness reflects runtime requirements. No regressions found in the follow-up delta.
Findings
- No blocking findings.
Validation
- Inspected
git diff 16a8b45b4278e16fc5f0f4efcbbf1c0d582a765f..HEADand affected surrounding context. git diff --checkpassed for both prior-head..HEAD and main..HEAD.git merge-treeagainst current main reported no conflicts.- Minimal Jinja probe confirmed the notebook phase map renders as valid JSON.
- Focused pytest could not start because the read-only checkout prevents editable package metadata creation; current-head GitHub CI is fully green.
Residual risks / optional notes
- None identified.
Automated review by Codex 4fdd0c60d1cc using IcebergAutoReview.
The four P0 items from the Claude Design UX review, as template + CSS + Alpine changes on the existing stack. No route removed, no asset pin touched, no new design tokens (one new class,
.marking-chip).Editor — markings promoted to the header
Intel level, TLP and analytic confidence move out of the Citations dock footer into always-visible header chips, so a product's markings are one source of truth and are legible while writing rather than behind a tab.
Each chip lays its real
<select form="reportform">transparently over itself, so a click opens the native dropdown: no popover code, keyboard- and screen-reader-navigable, and correct before Alpine hydrates. The component only mirrors the chosen option's own text back into the chip, so there is no label map to drift from the enums.Dashboard — actionable
Every KPI card links to the work behind it, and a "Needs you now" queue leads the page: the review queue (for anyone who can review) ahead of the viewer's own drafts, capped at five. Both halves are existing lifecycle state (
Report.status/author_id) surfaced as actions — no new model, no new query pattern. Notebook creation is demoted to a collapsed<details>so content leads.Admin — Settings & integrations hub (
GET /admin)One tile per configurable subsystem with a live status pill, grouped Outbound integrations / Governance. Status comes from
effective_config.admin_hub_tiles(), which reads the same settings singletons the config pages edit, so the hub can never disagree with the page it links to. It owns no state and has no save path.The pill deliberately separates OFF (opt-in integration disabled) from NOT CONFIGURED (enabled but missing an endpoint or its env-only secret) — the second is the state an operator most needs flagged, because it looks on but cannot deliver. Secrets are consulted as set/not-set only, never rendered.
Notebook — phase tabs
Nine stacked sections become Collect / Analyze / Produce / Trace, one phase at a time, with the old anchor-link collection strip re-cut as the tab bar (same per-section counts).
Nothing is gated: the tabs are real
#sectionanchors, Collect is the server-rendered default, and a<noscript>rule cancelsx-cloakso a browser without Alpine still gets every section on one page — exactly the previous behaviour.Verification
Full suite green (601 passed, 11 skipped) plus
ruff/bandit/vulture/mypy/djlint/biome. New tests cover the admin-only gate and every status-pill transition, the dashboard queue's ownership and ordering rules, the editor's single set of marking controls, and that all nine notebook sections still render. Also smoke-run against the real app per role.🤖 Generated with Claude Code
Update (rebase). The base branch originally duplicated the effective-config feature that had already merged as #263; it now carries only the AI-tile fix from #264, and this branch is rebased on top of it.
While restacking I applied the review-bot finding from #264 to the hub tile added here, which had the identical defect:
admin_hub_tilesreadAISettings.backendoff the row and would have shown a greenOPENAIpill whileai_settings.resolve()fail-closed tonone. A selected-but-invalid provider now reads NOT CONFIGURED with the rejection reason in the tile meta — consistent with how the same tile treats an enabled-but-unconfigured MISP or webhook. Covered by a new test that assertsresolve()really returnsnonefor the fixture.