Skip to content

[STORY] Wiki Metadata Fields Configuration via Web UI #323

@jsbattig

Description

@jsbattig

Story: Wiki Metadata Fields Configuration via Web UI

Feature: Wiki Metadata Fields Configuration

Part of: #EPIC

[Conversation Reference: "Groups findings 1,2,3,5 -- header block parsing, article_number/original_article, publication_status, views seeding from front matter"]
[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 configure which wiki metadata fields are recognized and displayed through the Config Web UI,
So that I can tailor the wiki to my project's content structure without code changes.

Objective: Introduce a WikiConfig dataclass with 4 boolean toggles controlling the 4 knowledge-base-specific metadata behaviors (header block parsing, article number, publication status, views seeding). All toggles default to True to preserve current behavior. The Config Web UI exposes a new "Wiki" section for these settings. WikiService methods accept a wiki_config=None parameter and conditionally skip KB-specific logic when the corresponding toggle is OFF.

User Value: Administrators of non-Salesforce wiki deployments can turn off KB-specific artifacts (article numbers, publication status badges, legacy header parsing, view count imports) to get a clean generic wiki experience. Existing deployments are unaffected because all defaults match current behavior.

Acceptance Criteria Summary: Four boolean toggles in Config Web UI control four KB-specific behaviors; all ON = current behavior; all OFF = clean generic wiki; config persists and takes effect without restart.

Implementation Status

  • WikiConfig dataclass defined in config_manager.py
  • WikiConfig added as nested field on ServerConfig with __post_init__ auto-migration
  • ServerConfigManager.load_config() handles wiki_config dict-to-dataclass conversion (inline pattern)
  • Config Web UI "Wiki" section added to config_section.html
  • ConfigService and web/routes.py handle Wiki config load/save
  • wiki/routes.py adds _get_wiki_config() helper passing config to WikiService methods
  • wiki_service.py render_article() conditionally skips _strip_header_block() when enable_header_block_parsing=False
  • wiki_service.py prepare_metadata_context() excludes article_number/original_article when enable_article_number=False
  • wiki_service.py prepare_metadata_context() excludes publication_status when enable_publication_status=False
  • wiki_service.py populate_views_from_front_matter() becomes no-op when enable_views_seeding=False
  • Unit tests: toggle ON/OFF for each of the 4 settings
  • Golden regression test: default config = identical to current behavior
  • Clean generic test: all toggles OFF = no KB-specific artifacts
  • Config persistence round-trip test
  • Default config migration test
  • Code review approved
  • Manual E2E testing completed

Completion: 0/17 tasks complete (0%)

Algorithm

WikiConfig Dataclass:
  enable_header_block_parsing: bool = True
  enable_article_number: bool = True
  enable_publication_status: bool = True
  enable_views_seeding: bool = True

render_article(file_path, repo_alias, wiki_config=None):
  raw_content = read file
  metadata, content = strip_front_matter(raw_content)

  IF wiki_config is None OR wiki_config.enable_header_block_parsing is True:
    content = strip_header_block(content, metadata)
  # ELSE: skip header block parsing, raw markdown renders as-is

  title = extract_title(metadata, file_path)
  html = render_markdown(content)
  html = rewrite_image_paths(html, repo_alias)
  RETURN {html, title, metadata}

prepare_metadata_context(metadata, repo_alias, article_path, wiki_cache, wiki_config=None):
  fields = copy all frontmatter keys (excluding skip keys)
  normalize updated->modified, original_article->article_number, draft->visibility

  IF wiki_config is not None AND wiki_config.enable_article_number is False:
    REMOVE "article_number" from fields
    REMOVE "original_article" from fields

  IF wiki_config is not None AND wiki_config.enable_publication_status is False:
    REMOVE "publication_status" from fields

  IF wiki_config is not None AND wiki_config.enable_views_seeding is False:
    REMOVE "views" from fields  # Front-matter "views" is KB-specific ("Salesforce Views" label)

  format date fields
  add real_views from DB

  build (label, value) list:
    IF "article_number" in fields:
      # Only reaches here if enable_article_number is True or wiki_config is None
      prepend article_number as first item
    append remaining fields

  RETURN result list

populate_views_from_front_matter(repo_alias, repo_path, wiki_cache, wiki_config=None):
  IF wiki_config is not None AND wiki_config.enable_views_seeding is False:
    RETURN  # no-op

  # Current behavior unchanged below this point
  existing = wiki_cache.get_all_view_counts(repo_alias)
  IF existing:
    RETURN  # skip if records already exist
  FOR each .md file in repo_path:
    parse front matter, extract views, insert into DB

_get_wiki_config() helper in wiki/routes.py:
  config_service = get ConfigService from app state
  server_config = config_service.get_config()
  RETURN server_config.wiki_config

Acceptance Criteria

AC1: Header Block Parsing Toggle ON (Default)

Given the wiki_config.enable_header_block_parsing is True (default)
And an article has legacy header fields (Article Number, Title, Publication Status)
When the article is rendered via render_article()
Then the header fields are stripped from the body
And their values are extracted into metadata
And the article body renders without the raw header text

AC2: Header Block Parsing Toggle OFF

Given the wiki_config.enable_header_block_parsing is False
And an article has legacy header fields (Article Number, Title, Publication Status)
When the article is rendered via render_article()
Then the header fields are NOT stripped from the body
And the raw header text renders as part of the markdown content
And the metadata does NOT contain values extracted from the header block

AC3: Article Number Toggle ON (Default)

Given the wiki_config.enable_article_number is True (default)
And an article has "article_number" or "original_article" in front matter
When prepare_metadata_context() builds the metadata panel
Then the article_number/original_article field appears in the metadata panel
And it is displayed with the label "Salesforce Article"
And it appears as the first item in the panel

AC4: Article Number Toggle OFF

Given the wiki_config.enable_article_number is False
And an article has "article_number" or "original_article" in front matter
When prepare_metadata_context() builds the metadata panel
Then the article_number/original_article field is excluded from the metadata panel
And no "Salesforce Article" label appears

AC5: Publication Status Toggle ON (Default)

Given the wiki_config.enable_publication_status is True (default)
And an article has "publication_status" in front matter
When prepare_metadata_context() builds the metadata panel
Then the publication_status field appears in the metadata panel
And it is displayed with the label "Status"

AC6: Publication Status Toggle OFF

Given the wiki_config.enable_publication_status is False
And an article has "publication_status" in front matter
When prepare_metadata_context() builds the metadata panel
Then the publication_status field is excluded from the metadata panel
And no "Status" label for publication_status appears

AC7: Views Seeding Toggle ON (Default)

Given the wiki_config.enable_views_seeding is True (default)
And a wiki repo has .md files with numeric "views" in front matter
And no existing view records exist for this repo
When populate_views_from_front_matter() is called
Then the front matter views values are inserted into wiki_article_views
And the seeded view counts match the front matter values

AC8: Views Seeding Toggle OFF

Given the wiki_config.enable_views_seeding is False
And a wiki repo has .md files with numeric "views" in front matter
When populate_views_from_front_matter() is called
Then the method returns immediately as a no-op
And no records are inserted into wiki_article_views

AC9: Golden Regression -- Default Config Matches Current Behavior

Given the wiki_config uses all default values (all toggles True)
And an article has legacy header fields, article_number, publication_status, and views in front matter
When the article is rendered and metadata panel is prepared
Then the output is identical to the output produced by the current codebase without any wiki_config parameter
And header fields are stripped from the body
And article_number appears first in the metadata panel
And publication_status appears in the metadata panel
And views seeding populates the database

AC10: Clean Generic Wiki -- All Toggles OFF

Given the wiki_config has all 4 toggles set to False
And an article has legacy header fields, article_number, publication_status, and views in front matter
When the article is rendered and metadata panel is prepared
Then header fields render as raw markdown in the article body
And no article_number or "Salesforce Article" label appears in the metadata panel
And no publication_status or "Status" label appears in the metadata panel
And views seeding does not populate the database
And the metadata panel shows only generic fields (created, modified, author, visibility, real_views)
And the front-matter "views" field (labeled "Salesforce Views" in _METADATA_LABELS) does not appear
  Note: When enable_views_seeding is False, the "views" front-matter key is a KB-specific artifact.
  prepare_metadata_context() should exclude it from the panel alongside article_number and publication_status.

AC11: Config Web UI -- Wiki Section

Given an admin navigates to the Config Web UI
When the page loads
Then a "Wiki" section is visible
And it contains 4 checkbox toggles:
  - "Enable header block parsing" (checked by default)
  - "Enable article number field" (checked by default)
  - "Enable publication status field" (checked by default)
  - "Enable views seeding from front matter" (checked by default)
When the admin unchecks a toggle and saves
Then the config.json is updated with the new wiki_config values
And the next wiki request reflects the changed setting
And no server restart is required

AC12: Config Persistence Round-Trip

Given a WikiConfig with custom toggle values (some True, some False)
When the config is serialized to JSON and written to config.json
And the config is loaded back from config.json
Then all WikiConfig toggle values match the saved values exactly
And no data is lost or defaulted during the round-trip

AC13: Default Config Migration

Given an existing config.json that does NOT contain a "wiki_config" key
When ServerConfig is loaded from this config.json
Then ServerConfig.wiki_config is a WikiConfig instance with all default values
And enable_header_block_parsing is True
And enable_article_number is True
And enable_publication_status is True
And enable_views_seeding is True

AC14: Backward Compatibility -- wiki_config=None Parameter

Given a caller invokes render_article() without passing wiki_config (wiki_config=None)
When the article is rendered
Then the behavior is identical to the current codebase
And header block parsing runs unconditionally
And no error or exception is raised

Technical Implementation Details

Component Structure

src/code_indexer/server/utils/config_manager.py
  - Add WikiConfig dataclass (4 boolean fields, all default True)
  - Add wiki_config: Optional[WikiConfig] = None to ServerConfig
  - Add wiki_config initialization in ServerConfig.__post_init__
  - Add wiki_config dict-to-dataclass conversion inline in load_config() (same pattern as LangfuseConfig, PasswordSecurityConfig, etc.)

src/code_indexer/server/services/config_service.py
  - Add `_update_wiki_setting(config, key, value)` method for WikiConfig field updates
  - Add `elif category == "wiki":` dispatch branch in `update_setting()` (follows existing pattern, e.g. `_update_langfuse_setting`)

src/code_indexer/server/web/templates/partials/config_section.html
  - Add "Wiki" section with 4 checkbox toggles
  - Wire form submission to save wiki_config values

src/code_indexer/server/web/routes.py
  - Add "wiki" to `valid_sections` list in `update_config_section()` (line ~6612)
  - Handle wiki_config fields in config save endpoint
  - Pass wiki_config to `populate_views_from_front_matter()` call at line ~2530 (background thread in golden repo wiki toggle handler)

src/code_indexer/server/wiki/routes.py
  - Add _get_wiki_config() helper function (follows _get_wiki_cache() pattern at line 70)
  - Pass wiki_config to render_article(), prepare_metadata_context(), populate_views_from_front_matter()

src/code_indexer/server/wiki/wiki_service.py
  - render_article(): add wiki_config=None parameter, conditional _strip_header_block()
  - prepare_metadata_context(): add wiki_config=None parameter, conditional field exclusion
  - populate_views_from_front_matter(): add wiki_config=None parameter, conditional no-op

Testing Requirements

Unit Test Coverage

  • Header block parsing ON: articles with legacy headers produce extracted metadata fields
  • Header block parsing OFF: same articles produce raw markdown body, no extracted header metadata
  • Article number ON: article_number/original_article appear in metadata panel result
  • Article number OFF: article_number/original_article excluded from metadata panel result
  • Publication status ON: publication_status appears in metadata panel result
  • Publication status OFF: publication_status excluded from metadata panel result
  • Views seeding ON: front matter views populate database (existing behavior)
  • Views seeding OFF: populate_views_from_front_matter returns without inserting records
  • Golden regression: default WikiConfig output == output without wiki_config parameter (None)
  • Clean generic: all toggles OFF produces panel with only generic fields
  • Config persistence round-trip: WikiConfig survives JSON serialize/deserialize
  • Default config migration: missing wiki_config key in JSON produces WikiConfig with all defaults
  • wiki_config=None backward compat: all methods work identically to current behavior when wiki_config is None

Integration Test Coverage

  • Config Web UI checkbox state reflects saved wiki_config values
  • Saving config via Web UI updates config.json with correct wiki_config

E2E Test Coverage

  • Admin toggles header block parsing OFF, views article, verifies raw header text appears
  • Admin toggles article number OFF, views article, verifies no "Salesforce Article" label
  • Admin toggles publication status OFF, views article, verifies no "Status" field
  • Admin toggles all OFF, views article, verifies clean generic metadata panel

Definition of Done

Functional Completion

  • All 14 acceptance criteria satisfied with evidence
  • WikiConfig dataclass integrated into ServerConfig with auto-migration
  • Config Web UI "Wiki" section operational with 4 toggles
  • All 4 toggles produce correct ON/OFF behavior in WikiService

Quality Validation

  • >90% test coverage for WikiConfig-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 dataclass + Web UI + service logic + template
  • No broken functionality
  • Backward compatible: existing deployments unaffected

Priority: HIGH (establishes WikiConfig foundation for Story 2 (#325))
Dependencies: None (first story in epic)
Success Metric: Default config produces identical behavior; all toggles OFF produces clean generic wiki

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