Skip to content

Admin Settings IA stage A+B: card-targeted links and dependency announcements - #1304

Merged
Paul Lizer (paullizer) merged 2 commits into
microsoft:feature/admin-settings-iafrom
paullizer:admin-ia-stage-a
Aug 19, 2026
Merged

Paul Lizer (paullizer) merged 2 commits into
microsoft:feature/admin-settings-iafrom
paullizer:admin-ia-stage-a

Conversation

@paullizer

Copy link
Copy Markdown
Contributor

Stage A and B of the Admin Settings information architecture rework.

Targets the feature/admin-settings-ia staging branch. No cards move in this PR — this is the groundwork that makes moving them safe.

Why this comes before re-homing anything

Auditing the regroup surfaced a problem that had to be fixed first: cross-tab links are coupled to tab ids, so the regroup would break all twelve of them at once.

<a href="#workspaces" onclick="switchTab(event, 'workspaces-tab')">

switchTab looks up document.getElementById('workspaces-tab'). Rename or regroup that tab and the lookup returns null, no pane activates, and the only symptom is a console warning plus a URL hash pointing at nothing. Nothing fails loudly.

Two links were already broken this way. Citations referenced Enable Video File Support and Enable Audio File Support with #workspaces, and the prose said "see the Workspaces tab" — but both settings live under Search and Extract. Anyone following either link landed on the wrong tab.

Setting Actually lives in
enable_video_file_support video-intelligence-section (Search and Extract)
enable_audio_file_support ai-voice-chat-section (Search and Extract)

Stage A — links point at cards, not tabs

Links now name the card they want, and the owning tab is resolved from the DOM at click time:

<a href="#video-intelligence-section" data-admin-link="video-intelligence-section">
const pane = document.getElementById(cardId).closest('.tab-pane');
window.showAdminTab(pane.id);

Because the tab is derived rather than declared, cards can move between tabs and tabs can be renamed or regrouped without touching a single link. Card ids are the only contract — and the template split already proved those are stable, preserving all 110 byte-identically.

The destination card is briefly highlighted so it's obvious where you landed.

switchTab had no callers left and is removed, so the old coupling can't be reintroduced by copy-paste.

Stage B — settings announce what they need

Dependencies were previously communicated four different ways, none actionable:

Dependency How it was communicated
File Sync needs Redis Cache Static server alert + flash() after saving
FeedbackAdmin role needs User Feedback One sentence of prose
Backup source blobs need Enhanced Citations Tooltip
Web Search needs consent flash() after saving

A card now declares its prerequisite, and a shared module renders an inline notice with two ways to act — a mirror switch to satisfy it without leaving the tab, and a link straight to its card:

<div class="card" id="file-sync-section"
     data-requires="enable_redis_cache"
     data-requires-label="Redis Cache"
     data-requires-target="redis-cache-section"
     data-requires-mode="warn">

Three judgement calls worth reviewing

The mirror carries no name attribute. Admin Settings posts one form read by field name, so a second named control would submit the value twice. The mirror uses data-dependency-proxy-for and drives the canonical input instead — exactly one named control per setting is ever submitted. A test enforces this.

File Sync warns rather than blocks. The backend already accepts File Sync as requested and reconciles once Redis is ready (requested_enable_file_sync vs file_sync_effective_enabled). Hard-disabling would remove a deferred-intent path that exists on purpose. Everything else blocks.

Permissions is scoped. That card holds both the SafetyViolationAdmin and FeedbackAdmin toggles, and only the latter depends on User Feedback. Blanket-disabling would switch off an unrelated control, so it declares data-requires-scope="#require_member_of_feedback_admin". A test asserts this specific scoping, because getting it wrong is silent.

Deliberately not wired

  • Backup source blobs → Enhanced Citations — already handled in admin_data_management.js with its own lock message. A second mechanism could disagree with the first.
  • Multi-modal vision → multi-endpoint — this is a fallback, not a requirement. Vision uses Global Endpoints when available and the legacy GPT deployment otherwise. Gating it would misrepresent the behaviour.
  • Redis Key Vault auth → Key Vault storage — conditional on a select value, and its inline hint already links correctly after stage A.

The backend stays authoritative

Client-side gating is a usability layer; a disabled input is a courtesy, never a control. route_frontend_admin_settings.py still validates and flashes, and a test asserts that enforcement is still present so the notice can't be mistaken for it.

Also fixed

The User Agreement preview used the same guarded-reassignment shape already corrected for the Home Page Text preview, which reads as unsanitized to static analysis. Now sanitized inline at the sink — admin_settings.html passes a full-file XSS scan, not just a changed-lines scan.

The File Sync server alert is narrowed to the case the client genuinely can't detect (Redis enabled but missing URL or credentials), so it no longer double-reports with the live notice.

Verification

  • Field names and card ids unchanged: 462 names, 110 card ids identical
  • 75 functional test files covering admin_settings.html: 33 pre-existing failures before and after, identical sets
  • All 20 admin templates parse under Jinja
  • check_xss_sinks.py and test_xss_guardrails_checker.py pass locally
  • The dependency notice is built with DOM APIs and textContent, never string HTML

New tests

test_admin_card_links.py — every link target exists, every href fallback matches, no switchTab pattern returns, resolver is wired and derives the tab from the DOM.

test_admin_settings_dependencies.py — every prerequisite and link target resolves, the mirror can't double-post, Permissions is correctly scoped, and backend enforcement is still in place.

The first property in each is the one that matters: they fail the build if a card is renamed or removed without updating what points at it — precisely the failure mode that went unnoticed before.

Next

Stage C adds the group navigation level against the current 18 tabs, so grouping is proven before any card moves. Stage D re-homes cards one group per commit. Stage E splits system-settings-section and builds the Access and Roles roster on this module's proxy handling.

Docs: docs/explanation/fixes/ADMIN_SETTINGS_CARD_TARGETED_LINKS_FIX.md, docs/explanation/features/ADMIN_SETTINGS_DEPENDENCY_ANNOUNCEMENTS.md. Version 0.260.008.

Paul Lizer (paullizer) and others added 2 commits August 19, 2026 13:45
Cross-references between Admin Settings tabs named a tab button directly, for
example switchTab(event, 'workspaces-tab'). That couples every link to a tab
id, so a rename or regroup breaks it silently: the button no longer exists, no
pane is activated, and the only symptom is a console warning and a URL hash
pointing at nothing.

Two of the twelve links were already broken this way. Citations referenced
Enable Video File Support and Enable Audio File Support with #workspaces, and
the prose said to see the Workspaces tab, but both settings live under Search
and Extract. Those links sent admins to the wrong tab entirely.

Links now declare the card they want with data-admin-link, and the owning tab
is resolved from the DOM at click time via closest('.tab-pane'). Cards can move
between tabs, and tabs can be renamed or regrouped, without touching a link.
The destination card is briefly highlighted so the jump is obvious.

This matters now because the information architecture rework renames and
regroups most tabs, which would have broken all twelve links at once.

switchTab had no callers left and is removed, so the coupling cannot be
reintroduced by copy-paste. The new test fails if a link points at a card that
does not exist, if the href fallback stops matching, or if the old pattern
returns.

Also sanitizes the User Agreement preview inline at the sink. It used the same
guarded-reassignment shape already corrected for the Home Page Text preview,
which reads as unsanitized to static analysis; admin_settings.html now passes a
full-file XSS scan rather than only a changed-lines scan.

Field names and card ids unchanged at 462 and 110, and the 75 functional test
files covering admin_settings.html show the same 33 pre-existing failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Several settings only take effect when a different setting is enabled, and the
two often live in different tabs. That was communicated four different ways,
none of them actionable: a static server-rendered alert, a sentence of prose, a
tooltip, or a flash message only after saving. An admin could switch something
on and have nothing happen with no visible reason, and no indication of where
the prerequisite lived.

A dependent card now declares what it needs with data-requires, and a shared
module renders an inline notice with two ways to act: a mirror of the
prerequisite switch, so it can be satisfied without leaving the tab, and a link
to its card using the stage A resolver.

The mirror deliberately carries no name attribute. Admin Settings posts one
form read by field name, so a second named control would submit the value
twice; the mirror drives the canonical input instead and exactly one named
control per setting is ever submitted.

File Sync warns rather than blocks, because the backend already accepts it as
requested and reconciles once Redis is ready. Removing that would take away a
deferred-intent path the backend supports on purpose. The Permissions
dependency is scoped to the FeedbackAdmin toggle so the unrelated
SafetyViolationAdmin toggle in the same card stays usable.

Three candidate dependencies are deliberately not wired: backup source blobs
are already gated in admin_data_management.js, multi-modal vision falls back to
the legacy GPT deployment rather than requiring multi-endpoint, and the Redis
Key Vault hint is conditional on a select value and already links correctly.

The File Sync server alert is narrowed to the case the client cannot detect,
Redis enabled but missing URL or credentials, so the two messages no longer
overlap.

Client-side gating is advisory. The backend still validates and flashes, and a
test asserts that enforcement is still in place.

Field names and card ids unchanged at 462 and 110, and the 75 functional test
files covering admin_settings.html show the same 33 pre-existing failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit 0726aca into microsoft:feature/admin-settings-ia Aug 19, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant