Skip to content

[STORY] Configurable Metadata Panel Display Order #325

@jsbattig

Description

@jsbattig

Story: Configurable Metadata Panel Display Order

Feature: Metadata Panel Display Order

Part of: #EPIC

[Conversation Reference: "Groups finding 6 -- metadata panel display order prioritizes KB fields over generic wiki fields"]
[Conversation Reference: "User said 'defaults match current behavior for backward compatibility' and 'good regression tests for every conceptual change'"]

Story Overview

As a CIDX server administrator,
I want to control the display order of metadata fields in the article metadata panel,
So that I can prioritize the fields most relevant to my users.

Objective: Add a metadata_display_order setting to WikiConfig (comma-separated string) that controls the order in which fields appear in the metadata panel. The default value is "" (empty string), which preserves current behavior: article_number first (if present), remaining fields in dict iteration order (YAML frontmatter key order). When an admin sets a non-empty order string, prepare_metadata_context() sorts its output tuples according to the configured order. Fields not listed in the order string appear after the listed ones, sorted alphabetically. Fields disabled by Story 1 toggles are auto-excluded regardless of their presence in the order string.

User Value: Administrators can reorder the metadata panel to prioritize fields relevant to their team (e.g., "author,modified,created" for a development wiki) instead of the default order. The default (empty string) preserves current behavior exactly. Setting a custom order string gives full control over field positioning.

Acceptance Criteria Summary: Comma-separated order string controls field ordering; default empty string preserves current iteration order; unlisted fields appear after listed ones alphabetically; disabled fields auto-excluded; edge cases handled gracefully.

Implementation Status

  • metadata_display_order comma-separated string field added to WikiConfig with default "" (empty = preserve current iteration order)
  • prepare_metadata_context() updated to sort output tuples according to configured order
  • Unlisted fields appended after listed ones, sorted alphabetically
  • Disabled fields (from Story 1 toggles) auto-excluded regardless of order string
  • Config Web UI "Wiki" section extended with text input for display order
  • Unit tests: default order matches current field ordering
  • Unit tests: custom order reorders fields correctly
  • Unit tests: unlisted fields appear after listed ones alphabetically
  • Unit tests: disabled field exclusion regardless of order
  • Unit tests: empty order string, duplicate fields, nonexistent fields
  • Config persistence round-trip test
  • Code review approved
  • Manual E2E testing completed

Completion: 0/13 tasks complete (0%)

Algorithm

WikiConfig.metadata_display_order (comma-separated string):
  Default: "" (empty string = preserve current iteration order)

prepare_metadata_context(metadata, ..., wiki_config=None):
  # ... existing field building and conditional exclusion logic (Story 1) ...
  # At this point we have a list of tuples: [(label, value), ...]
  # Each tuple was built from a metadata key

  IF wiki_config is None:
    # Backward compat: use current ordering
    # article_number first (already handled by existing code), rest in iteration order
    RETURN result

  IF wiki_config.metadata_display_order is "" (empty or whitespace-only):
    # Empty order string = preserve current iteration order (same as wiki_config=None)
    RETURN result

  # Parse order string (only reached when admin explicitly sets a non-empty order)
  order_list = [
    field.strip().lower()
    FOR field IN wiki_config.metadata_display_order.split(",")
    IF field.strip()
  ]

  # Remove duplicates while preserving order
  seen = set()
  unique_order = []
  FOR field IN order_list:
    IF field NOT IN seen:
      unique_order.append(field)
      seen.add(field)

  # Build a key-to-tuple mapping
  # We need to track which metadata key produced each tuple
  # Modify result building to track keys alongside tuples
  keyed_result = []  # List of (metadata_key, label, value)
  ... (build keyed_result during field iteration) ...

  # Sort by configured order
  ordered_output = []
  remaining = list(keyed_result)

  FOR order_key IN unique_order:
    FOR item IN remaining[:]:  # copy to allow removal during iteration
      IF item.metadata_key == order_key:
        ordered_output.append((item.label, item.value))
        remaining.remove(item)
        BREAK  # Each field appears only once

  # Append unlisted fields sorted alphabetically by metadata key
  remaining.sort(key=lambda item: item.metadata_key)
  FOR item IN remaining:
    ordered_output.append((item.label, item.value))

  RETURN ordered_output

Note: Fields disabled by Story 1 toggles (enable_article_number=False, etc.)
are excluded BEFORE ordering runs, so they never appear in the result
regardless of their presence in metadata_display_order.

Acceptance Criteria

AC1: Default Order -- Empty String Preserves Current Behavior

Given the wiki_config.metadata_display_order uses the default value "" (empty string)
And an article has front matter with article_number, publication_status, created, modified, author, and visibility
When prepare_metadata_context() builds the metadata panel
Then the fields appear in the current codebase order:
  article_number first (if present), remaining fields in dict iteration order (YAML key order)
And this is identical to the behavior when wiki_config=None
And no custom sorting is applied

AC2: Custom Order -- Reordered Fields

Given the wiki_config.metadata_display_order is "author,modified,created,visibility"
And an article has front matter with author, modified, created, visibility, and article_number
When prepare_metadata_context() builds the metadata panel
Then the fields appear in this order:
  1. author
  2. modified
  3. created
  4. visibility
  5. article_number (unlisted, appended alphabetically)

AC3: Unlisted Fields -- Appear After Listed Ones Alphabetically

Given the wiki_config.metadata_display_order is "created,modified"
And an article has front matter with created, modified, author, visibility, and article_number
When prepare_metadata_context() builds the metadata panel
Then the fields appear in this order:
  1. created (listed)
  2. modified (listed)
  3. article_number (unlisted, alphabetically first)
  4. author (unlisted, alphabetically second)
  5. visibility (unlisted, alphabetically third)

AC4: Disabled Field Exclusion -- Regardless of Order

Given the wiki_config.enable_article_number is False (from Story 1)
And the wiki_config.metadata_display_order includes "article_number" in the string
And an article has article_number in front matter
When prepare_metadata_context() builds the metadata panel
Then article_number does NOT appear in the panel
And the remaining fields are ordered according to the configured order
And no gap or placeholder appears where article_number would have been

AC5: Disabled Publication Status -- Regardless of Order

Given the wiki_config.enable_publication_status is False (from Story 1)
And the wiki_config.metadata_display_order includes "publication_status" in the string
And an article has publication_status in front matter
When prepare_metadata_context() builds the metadata panel
Then publication_status does NOT appear in the panel
And the remaining fields are ordered according to the configured order

AC6: Empty Order String -- Preserves Current Iteration Order

Given the wiki_config.metadata_display_order is "" (empty string, which is the default)
And an article has front matter with article_number, created, author, visibility
When prepare_metadata_context() builds the metadata panel
Then fields appear in current codebase order:
  article_number first (if present), remaining fields in dict iteration order
And the result is identical to calling with wiki_config=None
And no custom sorting is applied

AC7: Duplicate Fields in Order -- Field Appears Only Once

Given the wiki_config.metadata_display_order is "created,author,created,modified"
And an article has front matter with created, author, modified
When prepare_metadata_context() builds the metadata panel
Then each field appears exactly once
And the order follows the first occurrence in the order string:
  1. created
  2. author
  3. modified

AC8: Nonexistent Fields in Order -- Silently Ignored

Given the wiki_config.metadata_display_order is "nonexistent_field,created,also_fake,modified"
And an article has front matter with created and modified
When prepare_metadata_context() builds the metadata panel
Then nonexistent_field and also_fake are silently ignored
And the panel shows:
  1. created
  2. modified
And no error or warning is logged for nonexistent fields

AC9: Config Round-Trip

Given a WikiConfig with metadata_display_order set to "author,visibility,created"
When the config is serialized to config.json and loaded back
Then the metadata_display_order value is "author,visibility,created" exactly
And no data is lost or defaulted during the round-trip

AC10: Config Web UI -- Display Order Text Input

Given an admin navigates to the Config Web UI "Wiki" section
When the page loads
Then a text input is visible for "Metadata Display Order"
And it is pre-populated with the current metadata_display_order value
And help text explains: "Comma-separated list of metadata field keys. Fields not listed appear after listed ones, sorted alphabetically."
When the admin changes the order and saves
Then the config.json is updated with the new metadata_display_order value
And the next wiki article view reflects the new field ordering

AC11: Backward Compatibility -- wiki_config=None

Given a caller invokes prepare_metadata_context() without wiki_config (wiki_config=None)
When the metadata panel is built
Then the field ordering matches the current codebase behavior
And article_number appears first (if present)
And remaining fields appear in iteration order
And no error or exception is raised

AC12: Golden Regression -- Default Config Matches wiki_config=None

Given the wiki_config uses all default values (metadata_display_order = "")
And an article has front matter with article_number, publication_status, created, modified, author, visibility, views
When prepare_metadata_context() is called with default wiki_config
Then the output is identical (same fields, same order) to calling prepare_metadata_context() with wiki_config=None
And article_number is first, followed by remaining fields in dict iteration order
Note: This is trivially satisfied because empty order string short-circuits to current behavior

AC13: Interaction with real_views from Database

Given the wiki_config.metadata_display_order is "real_views,created,modified"
And an article has real_views tracked in the database (view count > 0)
And the article has created and modified in front matter
When prepare_metadata_context() builds the metadata panel
Then the fields appear in this order:
  1. real_views (labeled "Views")
  2. created (labeled "Created")
  3. modified (labeled "Modified")

Technical Implementation Details

Component Structure

src/code_indexer/server/utils/config_manager.py
  - WikiConfig: add metadata_display_order field (comma-separated string, default "" = preserve current order)

src/code_indexer/server/wiki/wiki_service.py
  - Update prepare_metadata_context() to:
    1. Track metadata key alongside each tuple during construction
    2. After building all tuples, sort according to wiki_config.metadata_display_order
    3. Unlisted fields appended alphabetically
    4. Return sorted list of 2-tuples (label, value)

src/code_indexer/server/web/templates/partials/config_section.html
  - Add text input for metadata_display_order in Wiki section
  - Include help text explaining the format

src/code_indexer/server/web/routes.py
  - Handle metadata_display_order field in config save endpoint

Internal Data Structure During Ordering

To preserve the metadata key for ordering purposes while producing the final tuple output, prepare_metadata_context() internally uses a keyed intermediate structure:

@dataclass
class _MetadataItem:
    key: str        # e.g., "article_number", "created"
    label: str      # e.g., "Salesforce Article", "Created"
    value: str      # e.g., "KA-12345", "March 15, 2024"

The final output strips the key, returning only (label, value) tuples.

Testing Requirements

Unit Test Coverage

  • Default order (empty string): field ordering matches current codebase exactly (iteration order preserved)
  • Custom order: "author,modified,created" reorders fields correctly
  • Unlisted fields: appear after listed ones, sorted alphabetically by key
  • Disabled field exclusion: article_number in order string but enable_article_number=False -- excluded
  • Disabled field exclusion: publication_status in order string but enable_publication_status=False -- excluded
  • Empty order string: preserves current iteration order (same as wiki_config=None)
  • Duplicate fields: "created,author,created" -- each field appears once, first occurrence position
  • Nonexistent fields: silently ignored, no error
  • wiki_config=None: ordering matches current codebase (backward compat)
  • Golden regression: default wiki_config (empty order) == wiki_config=None order
  • real_views ordering: database-sourced field respects configured order
  • Config round-trip: metadata_display_order survives JSON serialize/deserialize

Integration Test Coverage

  • Config Web UI text input displays current metadata_display_order
  • Saving new order via Web UI updates config.json

E2E Test Coverage

  • Admin changes order to "author,created,modified", views article, verifies author appears first
  • Admin sets empty order, views article, verifies current iteration order preserved

Definition of Done

Functional Completion

  • All 13 acceptance criteria satisfied with evidence
  • Configurable ordering works for all metadata fields including real_views from DB
  • Disabled fields excluded regardless of order string presence
  • Edge cases handled: empty string, duplicates, nonexistent fields

Quality Validation

  • >90% test coverage for ordering-related code paths
  • All tests passing (unit, integration, E2E)
  • Code review approved
  • Manual testing validated with evidence
  • fast-automation.sh passes with zero failures

Integration Readiness

  • Story delivers working, deployable software
  • Full vertical slice: config field + service ordering logic + Web UI input
  • No broken functionality
  • Backward compatible: existing deployments see identical field ordering

Priority: LOW
Dependencies: Story 1 (WikiConfig dataclass and config injection pattern must exist)
Success Metric: Default order matches current behavior exactly; custom order reorders fields as specified; disabled fields excluded regardless of order string

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions