Table of Contents

Global Mapper Specification

Version: 2.0 Last Updated: February 15, 2026


Table of Contents

Part I: Foundation

  1. Overview
  2. Architectural Principles

Part II: Architecture

  1. Domain Model
  2. Cache Architecture
  3. Error Handling

Part III: UI/UX

  1. Window Lifecycle
  2. Duplicates Tab
  3. Progress Overlays
  4. Families Tab
  5. Line Styles / Object Styles / Materials Tabs
  6. Shared Parameters Tab
  7. Global Controls
  8. Plan & Commit Workflow
  9. Performance

Part IV: Features

  1. Deep Scan
  2. Type Mapping
  3. Preview Pane
  4. Sorting, Filtering, Threshold

Part V: Quality & Implementation

  1. Known Issues & Roadmap
  2. Quality Acceptance Criteria

Part I: Foundation

1. Overview

1.1 Purpose and Scope

This specification defines the complete behavioral, architectural, and quality requirements for the Global Mapper (GM) tool. It serves as the authoritative reference for:

  • Developers: Implementation guidance with detailed behavioral specifications
  • QA/Testers: Acceptance criteria and validation requirements
  • Product Owners: Feature completeness and user experience standards
  • AI Agents: Context for code generation and refactoring decisions

1.2 Tool Overview

Global Mapper is an advanced Revit plugin that enables bulk mapping and migration of:

  • Families → Families (with type, parameter, material, object style mappings)
  • Line Styles → Line Styles
  • Object Styles → Object Styles
  • Materials → Materials
  • Shared Parameters → Project parameters or embedded family parameters

The tool provides similarity-based matching, manual overrides, deep scanning for detailed family analysis, and a plan-first commit workflow.


2. Architectural Principles

These are the foundational, non-negotiable constraints that govern all GM code.

2.1 Core Invariants

Kernel-Only, Ids-Only Data Model

  • All domain data is stored as Revit element IDs only (integers)
  • No names or descriptive strings in core data structures
  • Names are resolved only when visible in UI, never during render loops
  • Data is section-gated: families, usage (line/object styles, materials), shared_params

Fail-Fast, No Fallbacks, No Silent Failures

  • No fallback logic anywhere in codebase
  • No broad exception catches (typed exceptions only at narrow boundaries)
  • No defensive defaults to hide incorrect state
  • Fix root causes, never add wrappers or UI patches
  • Surface all errors via overlays, banners, or typed exceptions
// WRONG - swallows exception
try { File.Delete(file); } catch { }

// CORRECT - logs and propagates
try { File.Delete(file); }
catch (Exception ex)
{
    _logger.LogError(ex, "Failed to delete cache file: {Path}", file);
    throw;
}

Section-Gated Loading

  • Data is built in sections: families, usage, shared_params
  • Sections load lazily on first tab visit (not all at startup)
  • Never return placeholder/fallback content if section is missing
  • Throw explicit error or build section exactly once

Always-Fresh Cache Architecture

  • Families, types, materials, usage, shared parameters are always queried fresh from Revit
  • No snapshot caching of live data
  • Deep scan cache only (global, name-keyed, cross-document portable)
  • UI state persistence is separate from data (tab, search, scroll, filters)

DDD Layering (Domain → Application → Infrastructure)

  • Domain layer: pure entities, value objects, no Revit types
  • Application layer: orchestrators, lifecycle, queries, read models
  • Infrastructure layer: Revit adapters, persistence repositories
  • UI layer: binds only to Application interfaces
DBTools.GM.App/Domain/*
DBTools.GM.App/Application/*
DBTools.GM.App/Infrastructure/*
DBTools.UI/ViewModels/GM/* (consumes Application only)

Roslyn analyzers prevent forbidden cross-namespace usage.


First Paint, Then Work; Single Overlay Paradigm

  • Window renders immediately (first paint) without running collectors
  • After paint, initial work runs via dispatcher under progress overlay with cancellation
  • Never double-overlay (only overlay long work)
  • Commands disabled while busy (IsBusy affects CanExecute)

2.2 Data Model Constraints

Ids-Only Persistence

  • All persisted data uses element IDs (integers)
  • Names/descriptors are UI-time hydration only
  • Deep scan cache uses name/signature composite keys for portability

Hydration on Demand

  • Resolve names only for visible rows
  • Target option lists are pre-built for current category only
  • Nested details remain lazy (never pre-built)

Write Policy

  • UI state writes only on window close (no mid-session writes)
  • Deep scan cache writes immediately on scan completion
  • No resume JSON during session (always-fresh eliminates need)

2.3 Error Handling Philosophy

Typed Exceptions at Boundaries

// Domain exceptions
GmKernelError (base)
  ├── GmSnapshotLoadError (snapshot parse/version mismatch)
  ├── GmCollectionError (adapter failures during bulk reads)
  ├── GmInvalidMappingError (invalid/unsupported plan operations)
  └── GmApplyStageError (with Stage name and inner failure aggregation)

SafeExecute Boundary Pattern

All top-level UI entrypoints execute under ISafeExecutor:

// Entrypoint wrapper
src/DBTools.Core/Execution/SafeExecutor.cs

// Used by GM view models
src/Tools/Common/GM/Shell/UI/ViewModels/GmShellViewModel*.cs

Error Surfaces

  1. Notification Banner — Stacked manager (up to 3 visible per anchor window), fade in/out, auto-close; overflow can be expanded temporarily
  2. Progress Overlay Errors — Overlay shows during long ops, surfaces errors inline
  3. Output Window — Live window with search, copy/clear/export, level filters, duplicate grouping
  4. Error Boundary (Window-Level) — UnobservedTaskException handling, banner on background failures

2.4 REV3 Reconciliation Contracts (2026-02-15)

The following contracts are authoritative for REV3 behavior and must remain in sync across VM, services, and XAML templates.

Duplicates planner + readiness contract

  • Duplicates planning is source-row centric (DuplicateGroupRow) with explicit Apply, Keep Target, and PersistSelection.
  • Apply defaults off and is session-only (never restored from persisted UI state).
  • Keep-target options are domain/category scoped (GetScopedTargetIds(...)); self-target marks keeper and forces Apply=false.
  • Commit-active duplicate mappings are exactly row.IsReady.
  • Restored-saved advisory mappings are exactly row.IsRestoredAdvisory and remain visible in conflicts without blocking commit.
  • One-time duplicate mappings (Apply && !PersistSelection) are excluded from success-mapping persistence writes.

Conflict detection/input contract

  • Duplicate conflict input is built from normalized duplicate edges derived from current row state (IsReady + IsRestoredAdvisory).
  • Blocking conflicts are commit-active only; restored-saved conflicts are advisory.
  • Conflict UI has split semantics:
    • HasConflicts = blocking only (used to gate Review Plan).
    • HasAnyConflicts = blocking + advisory (used to surface flyout and conflict details).

Category pipeline contract

  • Family category identity is queried per family (FamilyScanner.GetFamilyCategoryIdsAsync) and stored as real FamilyRecord.CategoryId.
  • Duplicates family grouping reads that category ID directly.
  • Category label resolution order is curated-first, then kernel category-name cache (EnsureCategoryNamesForIdsAsync fallback).
  • Shared Parameters duplicate sub-category grouping remains parameter-type-display based (not Revit family categories).

Families parameters hydration contract (Issue 19)

  • Expand path is fixed to: ToggleFamilyRowEnsureFamilyParamNamesAsyncRebuildParameterMatchesForFamily.
  • Scanner discovery remains FamilyScanner.GetMappableParameterNamesForFamilyAsync using RevitCompat.IsUserMappable.
  • Parameters tab is always rendered; when scanning is unavailable or no mappable names are detected, the tab shows a centered empty/disabled state instead of hiding.

SP duplicate authoring bridge (Issue 12)

  • Shared Parameter duplicate rows are actionable in Duplicates when Apply=true with a valid keep target.
  • Accepted SP duplicate selections synchronize into Shared Parameters authoring rows by (TypeDisplay, ToName) -> FromName and set Apply=true for the matched authoring row when no explicit user override exists.
  • Plan building no longer uses the BindingMap feasibility gate for SP duplicate processing; SP work is emitted through Shared Parameters authoring operations (BindSharedParameter / EmbedSharedParameter).
  • Shared parameter value copy supports compatible coercions (string <-> numeric, int <-> double) and fails fast on unsupported conversions.

Source: src/Tools/Common/GM/Shell/ViewModels/DuplicatesModels.cs:59 Source: src/Tools/Common/GM/Shell/ViewModels/DuplicatesPaneViewModel.cs:373 Source: src/Tools/Common/GM/Shell/ViewModels/DuplicatesPaneViewModel.cs:1915 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.UsageAndFamilies.cs:157 Source: src/Tools/Common/GM/Shell/Services/CommitReviewWindowService.cs:46 Source: src/Tools/Common/GM/Shell/Services/MappingConflictService.cs:29 Source: src/Tools/Common/GM/Shell/Services/IMappingConflictService.cs:15 Source: src/Tools/Common/GM/Shell/ViewModels/MappingPaneViewModel.cs:44 Source: src/Tools/Common/GM/Domain/ProjectStateBuilder.cs:114 Source: src/Tools/Common/GM/Features/Families/FamilyScanner.cs:77 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:279 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:850 Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:851 Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:337 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:1069 Source: src/Tools/Common/GM/Features/SharedParameters/SharedParameterAuthoringService.cs:358


Part II: Architecture

3. Domain Model

3.1 GMProjectState (Root Aggregate)

The kernel is the single source of truth for all domain data during a session.

namespace DBTools.GM.App.Kernel

public sealed class GMProjectState
{
    // Snapshot reference (for resume)
    public GMSourceSnapshot SourceSnapshot { get; init; }

    // Families by ID
    public IReadOnlyDictionary<int, GMFamilyRecord> Families { get; init; }

    // Usage indices (materials, line styles, object styles)
    public GMUsageIndex Usage { get; init; }

    // Shared parameter ledger
    public GMSharedParameterLedger SharedParameters { get; init; }

    // Tab UI state
    public GMTabStateCollection Tabs { get; init; }
}

3.2 GMFamilyRecord

public sealed class GMFamilyRecord
{
    public int ElementId { get; init; }              // FamilyId
    public int? CategoryId { get; init; }            // Optional category
    public string? Name { get; init; }               // Lazy via names service; UI-only cache
    public int InstanceCount { get; init; }

    // Child entities
    public IReadOnlyDictionary<int, GMTypeRecord> Types { get; init; }  // By SymbolId

    // Usage references
    public IReadOnlyCollection<int> Materials { get; init; }
    public IReadOnlyCollection<int> ObjectStyles { get; init; }
    public IReadOnlyCollection<int> LineStyles { get; init; }

    // Parameters (name → descriptor)
    public IReadOnlyDictionary<string, GMParameterDescriptor> Parameters { get; init; }

    // Deep scan state
    public GMDeepScanState DeepScanState { get; init; }
}

Parameter Inclusion Rules:

Parameters are filtered programmatically (no curated name lists). Include parameter if ALL of these are true:

  • Parameter.IsReadOnly == False
  • Definition.Name is not null/whitespace
  • If Definition is InternalDefinition, then BuiltInParameter == INVALID (exclude Revit built-ins)
  • De-duplicate by name across instance/type scopes
  • Annotate scope as Instance, Type, or Instance+Type

3.3 GMTypeRecord

public sealed class GMTypeRecord
{
    public int SymbolId { get; init; }
    public int FamilyId { get; init; }

    // Usage for this type
    public GMTypeUsage Usage { get; init; }  // Materials/styles referenced

    // Sampled parameter values (for SP mapping UI)
    public IReadOnlyDictionary<string, string?> ParameterValues { get; init; }

    // Deep scan payload
    public GMTypeDeepScan DeepScanPayload { get; init; }
}

3.4 GMUsageIndex

public sealed class GMUsageIndex
{
    public IReadOnlyDictionary<int, GMUsageRecord> LineStyles { get; init; }
    public IReadOnlyDictionary<int, GMUsageRecord> ObjectStyles { get; init; }
    public IReadOnlyDictionary<int, GMUsageRecord> Materials { get; init; }
}

public sealed class GMUsageRecord
{
    public int ElementId { get; init; }           // Style or material ID
    public IReadOnlyCollection<int> Families { get; init; }  // Family IDs referencing this element
}

Canonical Uniqueness:

  • One row per style/object style/material from usage section
  • No duplicates (duplicates indicate hydration or binding bugs)
  • Built once per session, cached in kernel

3.5 GMDeepScanState

public sealed class GMDeepScanState
{
    public bool IsDeepScanned { get; init; }
    public IReadOnlyList<int> SampleInstanceIds { get; init; }
    public IReadOnlyList<string> ParameterNames { get; init; }
    public IReadOnlyCollection<int> StyleRefs { get; init; }
    public IReadOnlyCollection<int> MaterialRefs { get; init; }
}

Deep Scan Augmentation:

  • Families discovered only via deep scan appear in usage lists with DS badge
  • Usage count remains 0 if no instances in project
  • DS badge distinguishes deep-scan-only from in-project usage

3.6 Section Gating Rules

Families Section:

  • Built on initial load for default curated category
  • Rebuilt on category change
  • Never contains all categories at once (lazy per category)

Usage Section:

  • Built on first visit to Line Styles, Object Styles, or Materials tab (or when plan building requires usage for material/style operations)
  • Split into line vs object styles using IStyleService
  • Cached for session (reused across tabs)
  • Stored into kernel Usage (SSoT) so planning can compute FamiliesAffected for style/material-only plans

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:2106
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:318
Source: src/Tools/Common/GM/Shell/Services/KernelStateService.cs:314
Source: src/Tools/Common/GM/Domain/Planning/PlanningService.cs:300

Shared Parameters Section:

  • Built on first visit to SP tab
  • Curated groups only (hard-coded list)
  • Cached for session

Lazy Loading Policy:

  • Tabs other than active: lazily loaded on first visit (overlay if needed)
  • Families and SP: reload on category change (overlay if needed)
  • Expanded row details: always lazy (on "+" and/or after "Scan Family")

3.7 Element Uniqueness Guarantees

Families:

  • One GMFamilyRecord per unique FamilyId in selected category
  • Types: One GMTypeRecord per SymbolId under each family

Usage:

  • One GMUsageRecord per unique line style / object style / material ID
  • No duplicates across tabs (line styles ≠ object styles ≠ materials)

Shared Parameters:

  • One group per SharedParameterFile group
  • One definition per SP within group

Violation Handling:

  • Duplicates indicate collector or hydration bug
  • Must throw GmCollectionError with details
  • Never silently dedupe or use fallback

4. Cache Architecture

4.1 Always-Fresh Data Model

Live Data (Always Fresh from Revit):

  • Families (for current category)
  • Types (for loaded families)
  • Materials (project + linked)
  • Line Styles (project + linked)
  • Object Styles (project + linked)
  • Shared Parameters (from SharedParameterFile)

Querying:

  • Every tab load queries Revit API fresh
  • Session in-memory cache for instant tab switching within session
  • No staleness verification needed (data is current by definition)

4.2 Deep Scan Cache (Global, Name-Keyed)

Persistence:

  • File: deep-scan-cache.json in the DBTools cache directory (global, NOT document-specific)
  • Format: JSON (portable payload; no host element IDs persisted)

Composite Key:

public sealed class DeepScanCacheKey
{
    public string FamilyName { get; init; }
    public int TypeCount { get; init; }
    public string ParameterSignature { get; init; }  // Hash of fast-scanned mappable parameter base-names
}

Payload:

public sealed class DeepScanCache
{
    public DeepScanCacheKey Key { get; init; }
    public IReadOnlyList<string> ParameterNames { get; init; }
    public IReadOnlyList<GraphicsStyleKey> StyleKeys { get; init; }        // portable style identifiers
    public IReadOnlyList<string> MaterialNames { get; init; }             // portable material identifiers
    public IReadOnlyList<string> NestedFamilyNames { get; init; }         // portable nesting identifiers
    public DateTime CachedAt { get; init; }
}

Write Semantics:

  • Deep scan completion writes immediately to deep-scan-cache.json
  • Enables cross-session and cross-document reuse

Restore Semantics (Strict):

  • Cache restore is all-or-nothing:
    • Cache-key mismatch → cache miss
    • Any cached style/material/nested-family that cannot be resolved cleanly to exactly one host element ID → cache miss
  • No partial restore and no silent stale-data usage.

4.3 Cache Miss Policy (No Staleness Badge)

Mismatch Scenarios (treated as cache miss):

  • Family renamed in external project
  • Types added/removed (TypeCount mismatch)
  • Parameters added/removed (ParameterSignature mismatch)
  • Cached portable usage identifiers no longer exist or are ambiguous in the current document

UI Behavior:

  • No separate staleness badge; instead, mismatches force a rescan when the user triggers deep scan.
  • Corrupt/invalid cache entries are dropped during load, and a one-time "Deep Scan Cache Notice" alert is shown.

4.4 UI State Cache (Document-Scoped)

Persistence:

  • File: .gmstate_{documentKey}.json per document
  • Format: JSON with UI preferences

Content:

public sealed class GmUiState
{
    public string DocumentKey { get; init; }
    public int Version { get; init; } = 1;  // Strict version checking

    // Tab state
    public int SelectedTabIndex { get; init; }
    public int? SelectedCategoryId { get; init; }

    // Filters
    public bool FilterUnused { get; init; }
    public bool FilterApplied { get; init; }
    public bool FilterUnscanned { get; init; }
    public string? SearchText { get; init; }
    public int SimilarityThreshold { get; init; }
    public string SortMode { get; init; }  // "Similarity" | "Alphabetical"

    // Scroll positions
    public double? FamiliesScrollOffset { get; init; }
    public double? StylesScrollOffset { get; init; }
    public double? MaterialsScrollOffset { get; init; }
    public double? SPTypesScrollOffset { get; init; }
    public double? SPParamsScrollOffset { get; init; }

    // Expanded state
    public IReadOnlyList<int> ExpandedFamilies { get; init; }
    public int? SelectedSPTypeSymbolId { get; init; }

    // SP selections
    public IReadOnlyList<SPSelection> SPSelections { get; init; }
}

public sealed class SPSelection
{
    public int SymbolId { get; init; }
    public IReadOnlyList<SPParamSelection> Params { get; init; }
}

public sealed class SPParamSelection
{
    public string Name { get; init; }
    public bool Apply { get; init; }
    public bool IsHostLevel { get; init; }
    public bool? IsTypeBinding { get; init; }
    public string? MapFromName { get; init; }
}

Write Policy:

  • Only on window close (no mid-session writes)
  • Exception: User-initiated "Save Now" if added (explicit, not automatic)

Version Checking:

  • CurrentVersion = 1 (increment on schema change)
  • On load, if version mismatch → throw GmSnapshotLoadException
  • UI proceeds with fresh state (no fallback to old schema)

5. Error Handling

5.1 No Fallbacks Policy

Absolute Prohibitions:

  1. No fallback logic anywhere in codebase

    // WRONG
    var names = _kernel?.FamilyNames ?? new Dictionary<int, string>();
    
    // CORRECT
    if (_kernel == null)
        throw new InvalidOperationException("Kernel must be initialized before building plan context.");
    var names = _kernel.FamilyNames;
    
  2. No broad catches — typed exceptions only

    // WRONG
    try { ... } catch (Exception) { /* silent */ }
    
    // CORRECT
    try { ... }
    catch (GmCollectionError ex)
    {
        _logger.LogError(ex, "Failed to collect families for category {CategoryId}", categoryId);
        throw;
    }
    
  3. No silent failures — user must see what went wrong

    // WRONG
    try { File.Delete(file); } catch { }
    
    // CORRECT
    try { File.Delete(file); }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to delete cache file: {Path}", file);
        throw;
    }
    
  4. No defensive defaults — fix root causes

  5. No wrapper shortcuts — address problems at source


5.2 Typed Exceptions

Exception Types (standalone, no inheritance required):

Each exception type is a standalone sealed class with an error code for programmatic handling. See KernelExceptions.cs for complete implementation.

namespace DBTools.GM.Kernel;

// Error codes for kernel exceptions (1xxx=Build, 2xxx=Mapping, 3xxx=Invariant, 4xxx=Snapshot, 5xxx=Collection/Apply)
public enum GmKernelErrorCode { ... }

// Kernel build failures (name resolution, shared params, etc.)
public sealed class GmKernelBuildException : Exception
{
    public GmKernelErrorCode Code { get; }
}

// Invalid mapping operations
public sealed class GmInvalidMappingException : Exception
{
    public GmKernelErrorCode Code { get; }
}

// Snapshot load failures
public sealed class GmSnapshotLoadException : Exception
{
    public GmKernelErrorCode Code { get; }
}

// Invariant violations (invalid IDs, negative counts)
public sealed class GmInvariantViolationException : Exception
{
    public GmKernelErrorCode Code { get; }
}

// Adapter/collection failures when retrieving data from Revit
public sealed class GmCollectionError : Exception
{
    public GmKernelErrorCode Code { get; }
    public string CollectorName { get; }
}

// Apply stage failures with aggregated inner exceptions
public sealed class GmApplyStageError : Exception
{
    public GmKernelErrorCode Code { get; }
    public string StageName { get; }
    public IReadOnlyList<Exception> InnerFailures { get; }
}

5.3 SafeExecute Integration

Pattern:

// All top-level UI entrypoints execute under ISafeExecutor
await _runner.RunAsync("GM Apply - Full Plan", async () =>
{
    // ... application logic that may throw typed exceptions
});

Behavior:

  • Catches all exceptions from inner lambda
  • Logs to centralized logger
  • Shows banner notification to user
  • Never masks or silently swallows

5.4 Overlay Error Surfacing

When Used:

  • Initial tab load after paint
  • Category change (Families, SP)
  • Deep scan
  • Refresh/rebuild operations

Behavior:

  • Shows titled steps with ticks
  • Throttled progress updates
  • Cancel button sets cancel_requested (collectors honor it)
  • On error: show error message in overlay, do NOT close window

Never:

  • Double-overlay (check IsBusy before showing)
  • Close window on error (surface via overlay, let user decide)

Part III: UI/UX

6. Window Lifecycle

6.1 Entry and First Paint

Behavior:

  1. Entry: Global Mapper button clicked → window opens (modal)
  2. First Paint: Show window immediately WITHOUT running collectors
  3. Loaded Event:
    • Restore UI state from .gmstate file (tab, search, sort, threshold, filters, scroll offsets)
    • Use dispatcher to start initial loads AFTER paint completes

Critical: Window renders before any data collection begins.


6.2 Initial Load Sequence

After First Paint:

  1. Dispatcher-posted initial work under progress overlay:

    "Loading Families..."
    "Building usage indices..."
    "Resolving names..."
    
  2. Default tab (or restored tab from UI state) loads first

  3. Other tabs lazy-loaded on first visit

  4. _initial_load_complete flag set when default tab ready

  5. Bottom status line updates continuously with:

    • Global: section readiness (Families/Usage/Shared Parameters)
    • Active: tab-local state (loading/visible/apply-selected context)

Gating:

  • SelectionChanged events ignored until _initial_load_complete = true
  • Commands disabled while IsBusy = true

6.3 Tab Lazy Loading

Policy:

  • Families Tab: Load on first visit (or immediately if default tab)
  • Line Styles / Object Styles / Materials: Build usage section on first visit, then cache
  • Shared Parameters: Build SP ledger on first visit, then cache

Category Change:

  • Families tab: Reload families for new category (show overlay)
  • SP tab: Reload family/type tree for new category (show overlay)

Never:

  • Reload tabs on revisit (data cached in session)
  • Show overlay on tab switch if data already cached
  • Show static "No data" placeholders during first-load transitions (use explicit loading copy first)

6.4 Close Behavior

On Window Close:

  1. Deep scan cache: Already written mid-session (global deep-scan-cache.json file)
  2. UI state: Write .gmstate_{documentKey}.json with all UI preferences
  3. Session cache: Cleared (in-memory only, not persisted)

No mid-session writes for UI state.


7. Duplicates Tab

The Duplicates tab is the FIRST tab in the Global Mapper window.

7.1 Purpose

Identifies potential duplicate families, styles, materials, and shared parameters in the project using similarity algorithms. Users can accept suggested consolidations which become mappings applied during the Apply workflow.


7.2 Detection Service

Trigger:

  • Runs on tab activation or manual "Run Scan" button
  • Detection result is cached per session
  • Threshold/sort/search rebuild visible rows from cache (no re-detection)

Categories Analyzed:

  1. Families — Name similarity + parameter overlap
  2. Line Styles — Weight, color, pattern comparison
  3. Object Styles — Weight, color, pattern comparison
  4. Materials — ARGB, transparency, name comparison
  5. Shared Parameters — Type display + normalized name equality buckets (authoring-bridge flow)

Output:

  • Source-centric rows with a single explicit keep-target selection per source
  • Candidate details with scores (0-100) and rationale text

7.3 Similarity Algorithms

Naming Similarity (INamingSimilarityService):

double Similarity(string a, string b);      // Returns 0.0-1.0
string Normalize(string name);               // Case-insensitive, trim whitespace
bool TryGetNumericSuffixPair(string a, string b, out int aSuffix, out int bSuffix);

Algorithm:

  1. Numeric suffix priority: "Type 1" vs "Type 2" → high similarity (common duplicate pattern)
  2. Dice coefficient (bigram) fallback: General string similarity for non-numeric names
  3. Normalization: Case-insensitive, whitespace-trimmed comparison

Parameter Similarity (IParameterSimilarityService):

int ComputeScore(IReadOnlyDictionary<string, string> left, IReadOnlyDictionary<string, string> right);

Algorithm:

  • Jaccard index for parameter key overlap
  • Ignores Revit built-in parameters
  • Used for Families category scoring

Visual Property Similarity (Styles/Materials):

  • Line Styles: Line weight, color RGB, line pattern
  • Object Styles: Same as Line Styles
  • Materials: ARGB values, transparency percentage, name similarity

7.4 UI Structure

Summary Header (data-upfront):

  • Total, accepted, unresolved counts
  • By-kind counts (Families, Styles, Materials, Shared Parameters)
  • Top offenders (highest suggestion density)
  • Recommended next action text
  • Actions: Run Scan, Accept Visible

Main Grid (DuplicateGroupRow, source-centric):

Column Description
Kind Category (Families, Line Styles, etc.)
Source Source element name
Suggestions Count of candidate keep targets above threshold
Highest Score Best match percentage
Keep Target Single target to keep for this source
Status Needs review, Ready, Accepted, Ignored, Informational
Actions Accept source, clear keep selection

Details Pane (DuplicatePairRow):

Column Description
Keep Indicator for currently selected keep target
From Source display name
Candidate Candidate keep target display
Score Similarity percentage (0-100)
Rationale Explanation of match reason
Actions Set candidate as keep target

7.5 Commands

Run Scan:

  • Triggers detection for all categories
  • Shows overlay: "Detecting duplicates..."
  • Populates DuplicateGroups collection

Set Keep Target:

  • Updates single keep target on the source row
  • Syncs candidate Keep indicator in details pane

Accept Source:

  • Accepts selected keep target for one source row
  • Applies row locks to affected rows on other tabs
  • Updates duplicate selection persistence (_duplicateSelections)

Accept Visible:

  • Batch accept for all currently visible source rows with keep selection

Ignore Candidate:

  • Marks candidate ignored for that source and updates persisted duplicate selection

7.6 Row Locking

When a duplicate mapping is accepted:

  1. Source row on target tabs receives MappingOrigin = Duplicate
  2. Origin description/lock badge explains duplicate source and target
  3. User can unlock/override from the row lock interaction or by changing Duplicates selection

Lock Sources:

  • Families tab selections (existing)
  • Duplicates tab accepted mappings (this feature)

Locking is ROW-level only. There is no tab-level locking.


7.7 Persistence

Resume Snapshot Includes:

public IDictionary<string, int[]>? DuplicateGroupMembers { get; set; }  // key: kind:sourceId
public List<DuplicateSelection>? DuplicateSelections { get; set; }
public List<SharedParameterDuplicateSelection>? SharedParameterDuplicateSelections { get; set; }
public Dictionary<string, double> ScrollOffsets { get; set; } // includes "Duplicates"

Write Policy:

  • Persisted on window close only (single JSON)
  • No mid-session writes

Restore Behavior:

  • Rehydrate duplicate group membership, duplicate selections, and shared-parameter duplicate selections
  • Invalid group entries dropped with warning log
  • No silent fallbacks

8. Progress Overlays

8.1 When Shown

Always Show Overlay:

  • Initial tab load after first paint
  • Category change (Families, SP)
  • Deep scan
  • Apply plan execution
  • Any "long" collector (>500ms expected)

Never Show Overlay:

  • Tab switch if data already cached
  • Name resolution for visible rows (fast lookup)
  • Search/filter/sort (instant, no API calls)

8.2 Behavior

Display:

  • Titled steps with tick marks
  • Progress text: "Loading Families..." → "Resolving names..." → "Complete"
  • Throttled updates (avoid UI spam)

Cancellation:

  • Cancel button sets cancel_requested flag
  • Collectors honor flag and exit gracefully
  • Partial results discarded (fail-fast, no half-loaded state)

Error Handling:

  • On error: show error message in overlay (do NOT close window)
  • "Retry" button if operation is idempotent
  • "Close" button dismisses overlay and returns to main UI

8.3 Never Double-Overlay

Rule: Only ONE overlay at a time

Enforcement:

  • Check IsBusy flag before showing overlay
  • If already busy, queue operation or reject with message
  • Commands disabled via CanExecute while IsBusy = true

8.4 Throttled Progress Updates

Pattern:

// Update progress every N items or 200ms, whichever comes first
var progress = new Progress<string>(msg => BusyText = msg);

foreach (var chunk in items.Chunk(100))
{
    // Process chunk
    ((IProgress<string>)progress).Report($"Processing {processedCount}/{totalCount}...");
}

Rationale: Avoid overwhelming UI thread with rapid updates.


9. Families Tab

9.1 Category Dropdown

Data Source:

  • Hard-coded curated category list (single source of truth)
  • Exactly as currently implemented (no dynamic expansion)
  • Mapped to actual Revit CategoryId via Category.GetCategory or Settings.Categories

Implementation:

src/Tools/Common/GM/Features/Usage/Revit/Services/CategoryService.cs

Default Selection:

  • Visual default: "Annotation Symbols" (first curated label)
  • Never blank when rows are loaded

9.2 Loading Behaviors

Initial Load:

  1. After first paint, load families for default curated category ("Annotation Symbols")
  2. Show overlay with steps:
    "Collecting families..."
    "Counting instances..."
    "Resolving names..."
    
  3. Results appear with dropdown visually selected

Category Change:

  1. User selects different category from dropdown
  2. Show overlay (lazy load is acceptable)
  3. Reload families for selected category only
  4. Pre-build target option lists for current category

9.3 Row Structure

Columns:

  1. Expand (+ icon) — Shows details (Types, Object Styles, Materials, Parameters)
  2. Quantityinstance_count for family ID (0 if no instances)
  3. Apply — Checkbox (enables this family for mapping plan)
  4. Saved — Badge (if mapping preference saved for this family)
  5. Deep Scan Badge — single DS badge when family has been deep scanned (partial-scan warnings are shown in tooltip text, not a second badge variant)
  6. Source — Family name (resolved from kernel name cache)
  7. Target — Combobox with scored options (identity excluded)
  8. Search/Sort/Filters — Applied across all rows

9.4 Target Combobox Behaviors

Options Source:

  • Built from peer families in same category (identity excluded)
  • Pre-built at tab load for current category (cached for session)
  • Switching into combobox should be instant (no delay)

Similarity Mode:

  • Each option shows colored label (green → orange → red)
  • Similarity percentage appended: "Target Family Name (85%)"
  • Sorted high → low by score
  • Threshold influences default selection only (never override manual selection)

Alphabetical Mode:

  • Options sorted alphabetically
  • No similarity colors/scores (default styling)
  • No threshold filtering

Manual Selection Preservation:

  • Manual selection is sticky across re-sorts and threshold changes
  • Only reset if target disappears from options (e.g., family deleted)

9.5 Unscanned Family Demotion

Unscanned families appear beneath horizontal separator in target combobox:

  • Greyed out and disabled until scanned
  • Above separator: Ready options (selectable, colored in similarity mode)
  • Below separator: Unscanned options (disabled, requires deep scan)

After Scan:

  • Move to above separator
  • Enable selection
  • Apply normal scoring/coloring

9.6 Expand Details

With Instances (quantity > 0):

Shows:

  1. Types (from symbol_ids in kernel)
  2. Object Styles (usage-first; fast hydration by walking a single placed instance)
  3. Materials (usage-first; fast hydration by walking a single placed instance)
  4. Parameters (name mapping rows + example-value previews; deep-scan data is used when available, but deep scan is never auto-triggered)

Expansion flow is hybrid:

  • First pass ensures cached usage indices + fast parameter discovery.
  • If quantity > 0, GM augments expanded-row Object Styles/Materials by walking a single placed instance (including view-owned dependents like filled regions and group members) and updating the reverse indices used by the Families tab row-details.
  • Deep scan is user-initiated only (never automatic during expansion). If deep scan data already exists, it may still be used as augmentation, but expansion does not trigger scanning.

Expanded-row tab rendering:

  • Types and Parameters tabs are always rendered.
  • Object Styles and Materials tabs are always rendered, but disabled (muted/italic header) when their nested grids would be empty.
  • Parameters tab supports three states:
    • Scanner unavailable → disabled + message
    • No mappable names detected → empty-state message
    • Names present → grid enabled

Zero-Qty Gating (quantity = 0):

Shows:

  • "Scan Family" button
  • Hint text: "This family has no instances. Deep scan required to view details."
  • Details panel disabled until scan completes

After scan:

  • Details unlock
  • Types, Object Styles, Materials, Parameters appear
  • DS badge added to top-level row

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:2328
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:2828
Source: src/Tools/Common/GM/Features/Usage/UsageIndexService.cs:148


9.7 Nested Target Comboboxes

Child grids within expanded family rows have target comboboxes:

Types:

  • Options from target family's symbols
  • Same sort/threshold semantics as top-level

Object Styles:

  • Options from full universe of object styles
  • Identity excluded

Materials:

  • Options from full universe of materials
  • Identity excluded

Parameters:

  • Options from the target family's mappable parameter names (fast scanner when available; deep scan can augment when already present).
  • Sentinel options:
    • Blank (no mapping selected)
    • <Do Not Map> (explicitly opt out of mapping a row)
  • Parameter rows are scope-explicit:
    • Source parameter name rows are normalized into separate (Instance) and (Type) rows (no combined (Instance+Type) row).
    • Instance-scope sources only offer instance-compatible targets; type-scope sources only offer type-compatible targets.

Parameter conflict kinds (blocking):

  • ParamDuplicateTarget — More than one applied parameter row in a family maps to the same target parameter.
  • ParamTypeWriteAffectsExistingTargetInstances — Any applied (Type) parameter mapping requires a policy decision when the selected target type already has instances.

Type-write policy flow (for (Type) parameter mappings):

  • If the target type has no instances, type-scope parameter copies are allowed without a policy.
  • If the target type already has instances, GM requires a per-family policy before commit:
    • KeepTargetValues → skip type-scope writes
    • OverwriteTargetValues → overwrite target type values from the source
  • Policy is stored per family and persists as a saved-mapping entry (kind=param-type-policy).

Execution note (important):

  • Parameter migration operations (OperationKind.MapParameter) are executed during type replacements, adjacent to the ReplaceType work (not as a separate pre-pass).
  • Type-scope writes execute once per type replacement pair; instance-scope writes execute per affected instance.
  • Operation results are still recorded under the Parameters stage for reporting.

Lazy Loading:

  • Do NOT pre-build nested options (only top-level pre-built)
  • Build on expand or target change

Display/Edit Parity:

  • Nested target columns use the same display/edit pattern as top-level targets:
    • Read-only text rendering in normal grid state
    • SimilarityComboBox when editing
  • Similarity mode shows colored labels with percentage suffixes; alphabetical mode shows plain labels

Parent Target Change Behavior:

  • When a parent family target changes while the row is expanded, nested target rows (Types/Parameters/Object Styles/Materials) repopulate automatically
  • Rows manually edited by the user remain unchanged
  • Saved mappings and externally-originated/locked selections remain unchanged

Injected/restored options (DS badge):

  • When a saved mapping restores a target option that is not in the current option universe, GM injects that option into the list and marks it DS with a tooltip (visible in the ComboBox item template).

Source: src/Tools/Common/GM/Domain/Conflict.cs:87
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1914
Source: src/Tools/Common/GM/Shell/ViewModels/MatchingModels.cs:88
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:51
Source: src/Tools/Common/GM/Features/Mapping/Writers/TypeChangeService.cs:451


9.8 Parameter Pruning Rules

Include parameter if ALL of these are true:

  1. Parameter.IsReadOnly == False
  2. Definition.Name is not null/whitespace
  3. If Definition is InternalDefinition, then BuiltInParameter == INVALID (exclude Revit built-ins)

De-duplication:

  • De-duplicate by base name, but emit separate rows for each scope present:
    • Name (Instance)
    • Name (Type)

No curated name lists (programmatic filters only).

Source: src/Tools/Common/GM/Features/ParameterNameDiscoveryPolicy.cs:19
Source: src/Tools/Common/GM/Features/Families/FamilyScanner.cs:252
Source: src/Tools/Common/GM/Features/DeepScan/DeepScanServiceAdapter.cs:122 Source: src/Tools/Common/GM/Shell/Services/ParameterNameHelper.cs:32
Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:781
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:1859
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:2012


9.9 Cross-Tab Apply Lock

When Families tab sets Apply on Object Style or Material mapping:

  1. Corresponding top-level row on Object Styles or Materials tab reflects this
  2. Row is locked (Apply checkbox disabled with "F" badge)
  3. Un-apply ONLY from Families tab (not from locked tab)

Visual Indicator:

  • Blue "F" badge next to Apply checkbox
  • Tooltip: "Locked by Families tab mapping"

9.10 Preview Pane Toggle

Within expanded family details, when selecting:

  • Object Style row → Show/hide preview pane (swatch + style line)
  • Material row → Show/hide preview pane (color swatch + properties)

Behavior:

  • Preview appears on right side of window
  • Toggle via checkbox or auto-show on selection

9.11 Instance Navigation

Families and Shared Parameters both support instance navigation.

  • Families tab:
    • Expanded row Types panel provides prev/next instance cycling, Auto-zoom, and Zoom
    • Cycling refreshes parameter example values in the expanded Params grid
    • Zoom and Auto-zoom run with cross-view search and immediately activate/show the sampled instance
  • Shared Parameters tab:
    • Type details provide the same prev/next + zoom navigation pattern for sampled instances (cross-view search enabled)

Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:305
Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:405
Source: src/Tools/Common/GM/Shell/ViewModels/SharedParametersPaneViewModel.cs:467


9.12 Quantity vs Views Containing Pane

Quantity Column:

  • Reflects instance_count from kernel
  • 0 means no instances in project

Views Containing Selected Family (Right Pane):

  • Populated ONLY when quantity > 0
  • Shows all views containing selected family instances (EXCLUDES templates)
  • Double-click view → navigates and zooms to instance

Consistency Rule:

  • If quantity = 0 → pane should be EMPTY
  • If quantity > 0 but pane empty → bug (count miscalculation)

10. Line Styles / Object Styles / Materials Tabs

10.1 Row Sources

Data:

  • One row per style/object style/material from usage section of kernel
  • Canonical uniqueness: NO duplicates (violation = hydration bug)
  • Object-style source/target IDs may be either GraphicsStyle IDs or Category IDs; commit-time replacement resolves both forms to a canonical category/style reference.
  • Plan building normalizes selected style IDs to projection GraphicsStyle IDs before operations are emitted, preventing mixed category/style ID variants from producing unsupported style operations.
  • Object Styles tab scope is Detail Items subcategories only (non-line). Non-detail object styles are treated as out-of-scope and not eligible for planning.
  • Usage hydration enforces preview-universe filtering per tab (materials, line styles, object styles) so out-of-scope keys are dropped before rows are built.
  • Style scope resolution and normalization are shared between planning and style writer execution to keep accepted-pair classification identical at commit time.
  • Style apply-time scope resolution must execute with a gate-acquired active document (no ambient RevitAppContext dependency in ApplyAsync resolution).
  • When scope fallback is used (usage-backed line/object acceptance), logs must include fallback reason and source/target resolver reasons.
  • Material mappings are pre-validated against host-document material existence before planning emits operations.
  • Family reload calls (LoadFamily) for style/material/nesting/shared-parameter flows must execute via IRevitCallGate with no host-document transaction open (hostDoc.IsModifiable == false).

Source: src/Tools/Common/GM/Features/Mapping/Writers/StyleChangeService.cs:565 Source: src/Tools/Common/GM/Shell/ViewModels/UsagePaneViewModel.cs:298 Source: src/Tools/Common/GM/Features/Previews/StyleService.cs:83 Source: src/Tools/Common/GM/Features/Usage/UsageIndexService.cs:184 Source: src/Tools/Common/GM/Features/Mapping/StyleScopeResolver.cs:1 Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:142 Source: src/Tools/Common/GM/Features/Mapping/Writers/StyleChangeService.cs:49

Quantity:

  • Line Styles: Usage count (number of families using this style)
  • Object Styles: Families count (number of families using this object style)
  • Materials: Families count (number of families using this material)

Zero Quantities:

  • Visible (not filtered out)
  • Deep-scan-only families may contribute to usage with 0-qty (DS badge shown)

10.2 Target Options

Options Source:

  • Peer rows in current tab (identity excluded)
  • Same universe as source rows

Similarity Mode:

  • Colored labels (green → orange → red)
  • Similarity percentages: "Target Name (92%)"
  • Sorted high → low

Alphabetical Mode:

  • Default styling (no colors)
  • No percentages
  • Sorted lexicographically

Threshold:

  • Influences default selection only
  • Never overrides manual selection
  • Materials/Styles rows are NOT filtered by threshold (only option selection affected)

10.3 Validation (No Duplicates)

Enforcement:

  • Collector source must provide unique IDs
  • Hydration/binding must not duplicate rows

Violation Handling:

  • If duplicates detected → throw GmCollectionError
  • Never silently dedupe

10.4 Sorting Modes

Similarity:

  • Rows sorted by highest similarity to target (if target selected)
  • Or by usage count descending

Alphabetical:

  • Rows sorted by source name A-Z
  • Threshold ignored

Threshold Influence:

  • Threshold slider affects option list default selection only
  • Row visibility unaffected (all rows shown regardless of threshold)

10.5 Preview and Usage Lists

Selection Behavior:

Line Styles:

  1. Select row → preview updates (swatch and style line rendering)
  2. No "Families Using This ..." pane (line styles are host-document-only)

Object Styles:

  1. Select row → preview updates (swatch and style line rendering)
  2. "Families Using This Object Style" pane populates with family names
  3. Double-click family → navigate to view and show instance
  4. Checking Apply on the style row links Apply on affected family rows
  5. Unchecking Apply removes that link; family Apply remains only if still linked elsewhere or manually selected
  6. Scope is Detail Items object styles only; unsupported style pairs are dropped during plan build (warning banner) before commit
  7. During family apply, if target subcategory is missing in the family document, GM creates it under the resolved parent, syncs core appearance (color/weight/pattern), then rewrites references and reloads the family

Materials:

  1. Select row → preview updates (color swatch, texture, properties)
  2. "Families Using This Material" list populates
  3. Double-click family → navigate to view and show instance
  4. Checking Apply on the material row links Apply on affected family rows
  5. Unchecking Apply removes that link; family Apply remains only if still linked elsewhere or manually selected
  6. During family apply, if target material is missing in a family document, GM creates it and syncs core material properties before rewriting source references and reloading the family
  7. "Families Using This Material" apply indicators are driven by canonical usage-link state (OS/MAT) and remain accurate even when a family is not currently visible in the Families tab list/filter scope
  8. Unsupported material pairs (source/target not resolvable in the host document) are dropped during plan build (warning banner) before commit

Families Tab Link Indicators:

  • Linked family rows display source badges: OS, MAT, or OS+MAT
  • Badge reflects active usage-link sources, independent from manual family Apply toggles
  • Expanded family nested Object Styles/Materials grids show per-row link badges (LK with OS or MAT) for the exact source rows currently driving the family Apply linkage
  • Usage-linked Apply is treated as impact signaling for family-owned mappings:
    • Type replacements, parameter maps, and create-type operations are only planned when the family row is explicitly user-owned (manual Apply, user-edited, saved-origin, or external-origin), not from usage auto-link alone.

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:908
Source: src/Tools/Common/GM/Shell/ViewModels/MatchingModels.cs:190
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:1615
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:2123
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:2407
Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:231
Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:86


10.6 DS Badges

When Shown:

  • Row has any DS-scanned families in usage list
  • Badge: "DS" icon or text
  • Tooltip: "Includes deep-scanned families"

Deep Scan Augmentation:

  • Families discovered ONLY via deep scan appear in "Families Using This ..." lists (Object Styles, Materials)
  • DS badge distinguishes them
  • Usage count remains accurate (0 if no instances)

11. Shared Parameters Tab

11.1 Layout

Left Panel:

  • Groups from SharedParameterFile
  • Definitions within selected group
  • Browse-only (no mutations)

Right Panel (Main Grid):

  • Families → Types → Parameters tree for selected curated category
  • Family rows expand/collapse via + command routing on the SharedParameters pane viewmodel
  • Single-selection semantics (only one type expanded at a time)

11.2 Group and Category Selectors

Group Dropdown:

  • Populated from SharedParameterFile (or pre-collected snapshot)
  • Default: First group alphabetically
  • Never blank

Category Selector (Tree, Level 0):

  • Hard-coded curated category labels (Walls, Doors, Mechanical Equipment, Windows)
  • Always visible (even when count is 0)
  • Label includes count like Mechanical Equipment (12); 0-count categories are dimmed
  • Category content is loaded for the currently selected category (tree children are populated for selected only)

Loading:

  • First visit to the tab lazily loads SP groups and prewarms authoring snapshots for all curated categories
    • Ensures category counts are correct immediately
    • Makes switching categories instant (cache hit)
  • After initial lazy-load completes, category/group changes rebuild authoring without invoking the progress overlay

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:755
Source: src/Tools/Common/GM/Shell/ViewModels/SharedParametersPaneViewModel.cs:487
Source: src/Tools/Common/GM/Shell/ViewModels/SharedParametersPaneViewModel.cs:1672


11.3 Main Grid Scoping

Data Sources:

  • shared_params section: powers left panel (groups/definitions)
  • families section: powers family/type tree (right grid) for selected category

Scoping:

  • Grid shows ONLY families in selected curated category
  • Each row: Family → Types → Parameters

Empty Grid:

  • If no families in curated category → show clear banner
  • "No eligible families in selected category"
  • Keep categories curated (do NOT expand dynamically)

11.4 Type Details Expansion

Header (top row, right of SP Group dropdown):

  1. Instance Navigator: Left/right arrows (cycles instances)
  2. Instance Count: "3/27"
  3. Auto-zoom: Toggle for cycling behavior
  4. Zoom: Navigates and highlights current instance

Parameter Grid (below header):

Section 1: Existing Parameters

  • Type and instance parameters for selected type
  • Current values from sampled instances (sampling cap applies)
  • NOT editable (read-only display)

Separator

Section 2: Shared Parameters from Selected Group

  • Each SP row:
    • Apply Checkbox: Enables mapping
    • End-State Toggle: Host-level vs Embed-in-family
    • Map From Dropdown: Existing parameters with values (scored by name similarity + coverage)
    • Current Value: From visible instance
    • Proposed Value: From "Map From" parameter
    • Diff Styling: Old struck-through, new in red/bold when different

11.5 Instance Navigation

Instance navigation is available on the Shared Parameters tab only.

Behavior:

  1. Navigator appears inside SP type rows when instances sampled
  2. Prev/Next arrows cycle through instances
  3. Current and Proposed values update live on cycle
  4. Proposed turns red/bold when different from Current

Families Tab:

  • Does NOT have instance navigation
  • Shows quantity and "Views Containing" list only

11.6 Scoring and Defaults for "Map From"

Algorithm:

  • Combine name similarity (same as Families tab matching) with value coverage
  • Coverage = % of sampled instances with non-empty values
  • Highest combined score preselected

Manual Override:

  • User can change "Map From" selection
  • Selection preserved across refreshes

11.7 Visual Indicators

Badges:

  1. Host-level bound: Badge if SP already exists as project parameter
  2. Embedded: Badge if SP already embedded in family
  3. Deep Scan: DS badge if deep scan data available for this family
    • No DS! variant; partial scan state is exposed via tooltip copy

Gating Label:

  • "Deep Scan required" shown when reading embedded schema needed (e.g., unplaced types)

11.8 Apply Semantics

Host-Level:

  1. Bind SP to Category (Type or Instance per SP definition)
  2. Copy values from selected "Map From" parameter
  3. Batch operation (all checked SPs in one transaction)
  4. Plan-time validation requires selected symbol IDs to resolve in kernel and align with current owner-family context

Embed-in-Family:

  1. Open family in background
  2. Add SP per definition (Type or Instance)
  3. Set default values
  4. Reload family
  5. Copy values from "Map From" parameter
  6. Plan-time validation rejects stale/mismatched symbol-family selections before commit is opened

No "Remove Source" Option:

  • Source parameter retained after copy
  • User must manually delete if desired

Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:489
Source: src/Tools/Common/GM/Features/SharedParameters/SharedParameterAuthoringService.cs:141


11.9 Persistence

On Close (UI State):

  • Selected SP type (SelectedSPTypeSymbolId)
  • SP Types and Parameters scroll offsets
  • Per-type parameter selections:
    • Apply checkbox state
    • Host-level vs Embed toggle
    • "Map From" selection by name

NOT Persisted:

  • Nested scroll positions inside type details
  • Sampled instance values (always fresh on load)

12. Global Controls

12.0 Status and Empty-State UX

Bottom Status Line:

  • Single-line format: Global: ... || Active: ...
  • Global summarizes Families/Usage/Shared Parameters readiness
  • Active summarizes the currently selected tab state

Empty-State Messaging Contract (all tabs):

  • Before first load completes: show explicit loading language (Loading...)
  • After load completes with zero rows: show contextual No ... messaging
  • Use high-contrast theme text brushes (primary title + secondary guidance)

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:867
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1035
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:1469
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:4619

12.1 Sort Mode

Options:

  • Similarity: Colored labels with percentages, sorted high→low, threshold-aware
  • Alphabetical: Default styling, lexicographic sort, threshold ignored

Applies To:

  • ALL target option lists (top-level and nested)
  • Families, Line Styles, Object Styles, Materials tabs

Behavior:

  • Switching mode updates ALL visible comboboxes instantly
  • Manual selections preserved (only re-sorted, not cleared)

12.2 Threshold Slider

Purpose:

  • In similarity mode: influences default selection only
  • Options below threshold NOT auto-selected
  • Manual selections NEVER overridden by threshold change

Range: 0-100 (similarity percentage)

Debounced: 200ms debounce to avoid UI spam

Applies To:

  • Families, Line Styles, Object Styles, Materials tabs
  • Top-level AND nested target option lists

Does NOT Apply To:

  • Row visibility (all rows shown regardless of threshold)
  • Alphabetical mode (threshold ignored)

Behavior:

  • Filters SourceName for active tab
  • Case-insensitive substring match
  • Real-time (no debounce needed for simple string filter)

Applies To:

  • Families, Line Styles, Object Styles, Materials, SP tabs

12.4 Filters

Available Filters:

  1. Hide Unscanned (Families only)

    • Hides families without deep scan data
    • Checkbox toggle
  2. Show Applied Only

    • Shows rows that are applied manually, externally-originated, or usage-linked from Object Styles/Materials
    • Applies to all tabs

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:255
Source: src/Tools/Common/GM/Shell/ViewModels/UsagePaneViewModel.cs:674
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:988

Persistence:

  • Filter states saved in UI state cache
  • Restored on window reopen

12.5 Clear Scans

Purpose:

  • Clears global deep-scan-cache.json cache file
  • Forces re-scan of all families

When to Use:

  • User knows families have changed significantly
  • Deep scan data appears outdated or incorrect

Behavior:

  • Deletes deep-scan-cache.json file
  • DS badges removed from all rows
  • Next deep scan rebuilds cache
  • Clears in-memory deep-scan usage caches and refreshes usage/family/SP views immediately
  • Shows success banner: "Deep scan cache cleared."

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:3322


12.6 Clear Preferences

Purpose:

  • Clears saved mapping preferences (saved target selections and related origin markers)

Behavior:

  • Clears in-memory commit success mappings and persisted saved mappings for the current document key
  • Rebuilds usage + family match rows so saved-origin badges are removed
  • On reopen, restored saved mappings seed target dropdowns only; Apply stays false by default until explicit user action
  • Resets saved Shared Parameter row selections (Apply, Map From, edited flags) during clear
  • Persists an explicit empty saved-mappings snapshot when mappings are cleared (overwrites stale mapping files)
  • Suppresses the next window-close saved-mappings write to prevent immediate re-persist of just-cleared mappings
  • Saved-mappings write failures are logged and surfaced via a user-visible error banner (no silent persistence failures)
  • Shows success banner: "Cleared saved mappings for this document."

Does NOT Clear:

  • Deep scan cache
  • UI state (tab, search, filters)

Saved mapping kinds + keying (owner-family scoped):

GM persists commit-success mapping targets as SavedMappingEntry records. Keying is kind-specific; the following kinds are explicitly scoped by OwnerFamilyId:

Kind Meaning Key fields
nested-style Expanded-family Object Style mapping (Kind, FromId=StyleId, OwnerFamilyId=FamilyId)
nested-material Expanded-family Material mapping (Kind, FromId=MaterialId, OwnerFamilyId=FamilyId)
param-name Expanded-family Parameter name mapping (Kind, FromId=StableStringId(SourceParamName), OwnerFamilyId=FamilyId)
param-type-policy Expanded-family type-write policy for (Type) parameter migration (Kind, FromId=FamilyId, OwnerFamilyId=FamilyId)

Source: src/Tools/Common/GM/Features/SavedMappings/SavedMappingEntry.cs:31
Source: src/Tools/Common/GM/Features/SavedMappings/SavedMappingEntry.cs:38
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:391
Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:486
Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:781

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1879 Source: src/Tools/Common/GM/Shell/Services/UiStatePersistenceService.cs:62 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:742 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:2017 Source: src/Tools/Common/GM/Shell/Services/UiStatePersistenceService.cs:90 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:2066 Source: src/Tools/Common/GM/Shell/ViewModels/UsagePaneViewModel.cs:676 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:487


12.7 Clear Saved UI State

Purpose:

  • Clears persisted GM UI state for the current document key.

Behavior:

  • Deletes per-document UI snapshot from cache
  • Suppresses the next close-cycle UI save so the cleared state is not immediately recreated
  • Shows success banner: "Cleared saved UI state. Next open will use defaults."

Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1932 Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:742


13. Plan & Commit Workflow

13.1 Plan Construction

User selections are tracked in live dictionaries throughout the session. When the user clicks Review Plan:

  1. BuildPlanAsync() normalizes all checked rows into categorized operations
  2. Plan operations are grouped by category and validated

Usage-Link Planning Guardrail:

  • Families auto-checked by Object Styles/Materials usage links are not automatically considered opted-in for family-owned operations.
  • Type replacements, parameter maps, and create-type operations require explicit ownership on the family row:
    • manual family Apply, or
    • user-edited, saved-origin, or externally-originated row state.
  • Material/style replacements remain host-level mappings and are still planned directly from Usage tab selections.

Shared Parameter Planning Semantics:

  • Shared parameter operations are built from SP tab definition rows with Apply=true and a selected SP category.
  • SP operation planning is independent of Families tab Apply ownership gating.

Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:80

Plan Categories:

Category Description
Parameters Parameter value migrations
TypeCreation New types requested from expanded-family type rows (Create=true)
HostReplacements Host-level element replacements
NestedFamilies Nested family swaps
TypeReplacements Family type replacements
StyleReplacements Line/Object style replacements
MaterialReplacements Material replacements
SharedParameters SP bindings and value copies

Validation:

  • Self-maps rejected (source == target)
  • Zero/negative IDs rejected
  • Invalid operations throw GmInvalidMappingError

13.2 Commit Review Window

When opened, Commit Review builds and validates the plan only. No document edits run until a commit action is selected.

Display Content:

  • Operation breakdown by category with counts
  • Plan summary and validation status before commit
  • Bottom-edge status bar with live metrics:
    • Rules — Planned / Applied / Failed / Skipped
    • Families — Planned / Attempted / Failed
  • Normalized details showing from → to mappings
  • Error indicators for any validation failures
  • Failed Operations uses a single canonical table surface summarized per mapping rule with per-row:
    • Stage, Operation, Source, Target
    • Message (aggregated error classifications + per-family context)
  • Domain Summary shows per-stage succeeded/failed/skipped/affected totals and includes family/timing metrics when available.
  • Report DataGrids use centered column headers and right-click context actions:
    • Copy Selected
    • Copy All
    • Copied row format: Header: Value | Header: Value ...
  • Expanders with no meaningful content are hidden (no placeholder clutter).

User Actions:

  • Close: Closes review without changing the model
  • Apply: Begins a staged TransactionGroup, executes apply + verify inside the group, and leaves the group open (pending accept/rollback).
  • Commit Review is modal for the entire apply/accept/rollback session to keep the transaction group open safely.
  • After apply completes, Commit Review stays open and shows results:
    • Rollback: Rolls back the transaction group (discard all changes from this apply session)
    • Accept + Continue: Assimilates the transaction group, closes Commit Review, and returns to GM (runs refresh pipeline and persists applied mappings)
    • Accept + Close GM: Assimilates the transaction group, closes Commit Review, and closes the GM tool (honors close even on partial failure)
  • Retry Apply: Shown when apply applied nothing and failures were detected. Retries using the existing plan.
  • Post-apply banner feedback:
    • Success: green summary banner with Applied/Failed/Skipped counts
    • Failure: banner beginning with "Apply completed." (includes Failed/Skipped diagnostics) or "Apply failed." when an exception occurs
  • Commit Review stays open on failed apply so users can inspect operation errors before choosing Retry/Accept/Rollback.
  • Accept + Continue performs an immediate in-session refresh:
    • evicts current Families category snapshot
    • re-scans families for the active category
    • refreshes usage indices when usage mappings are affected (or usage is already loaded)
    • restores expanded family/details state so instance cyclers continue to function on refreshed rows

Implementation:

src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs
src/Tools/Common/GM/Shell/Services/CommitReviewWindowService.cs
src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.UsageAndFamilies.cs
src/Tools/Common/GM/Shell/Services/FamiliesCategoryCacheService.cs

Source: src/Tools/Common/GM/Shell/Services/CommitReviewWindowService.cs:24
Source: src/Tools/Common/GM/Shell/Views/CommitReviewWindow.xaml:69
Source: src/Tools/Common/GM/Shell/Views/CommitReviewWindow.xaml:73
Source: src/Tools/Common/GM/Shell/Services/CommitReviewWindowService.cs:61
Source: src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs:274
Source: src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs:327
Source: src/Tools/Common/GM/Shell/Views/CommitReviewWindow.xaml.cs:60
Source: src/Tools/Common/GM/Shell/Views/CommitReviewWindow.xaml:318
Source: src/Tools/Common/GM/Features/Mapping/Writers/ChangeApplyEngine.cs:69


13.3 Execution Order

On commit, operations execute in strict order through ChangeApplyEngine:

  1. Create Types — Duplicate source symbols using NewTypeName from row-level create entries
  2. Host Replacements — Replace host-level elements
  3. Nested Families — Swap nested family references
  4. Type Replacements — Replace family instances by type (parameter migrations execute during each type replacement)
  5. Style Replacements — Apply host-document replacements and family-document rewrites for style references, then reload affected families
  6. Material Replacements — Apply host-document replacements and family-document rewrites for material references, then reload affected families
  7. Shared Parameters — Bind/Embed shared parameters and copy values

Transaction Management:

  • Writer failures are captured per operation and the apply run continues across remaining stages (non-fail-fast)
  • Group-owned family-document references (members inside groups) are skipped (cannot edit group members) and reported as warnings (non-fatal)
  • Unhandled exceptions/cancellation stop the commit flow; undo entry granularity depends on the underlying writer transaction boundaries
  • Family reload phases run outside host transactions (gate-only) because Revit rejects LoadFamily when the host document is modifiable.

Source: src/Tools/Common/GM/Features/Mapping/Writers/StyleChangeService.cs:203 Source: src/Tools/Common/GM/Features/Mapping/Writers/MaterialChangeServiceWriter.cs:209 Source: src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs:747 Source: src/Tools/Common/GM/Features/Mapping/Writers/StyleChangeService.cs:565 Source: src/Tools/Common/GM/Features/Mapping/Writers/NestingChangeService.cs:290 Source: src/Tools/Common/GM/Features/SharedParameters/SharedParameterAuthoringService.cs:318 Source: src/Tools/Common/GM/Features/Mapping/MappingApplyResult.cs:169 Source: src/Tools/Common/GM/Features/Mapping/MappingService.cs:382 Source: src/Tools/Common/GM/Features/Mapping/Writers/ChangeApplyEngine.cs:166 Source: src/Tools/Common/GM/Features/Mapping/MappingService.cs:413 Source: src/Tools/Common/GM/Shell/Startup.cs:57 Source: src/Tools/Common/GM/Features/Mapping/Writers/TypeCreationService.cs:30 Source: src/Tools/Common/GM/Features/Mapping/Writers/TypeChangeService.cs:190 Source: src/Tools/Common/GM/Features/Mapping/Writers/TypeChangeService.cs:259

Progress Overlay:

  • Shows current step name
  • Throttled progress updates
  • Cancel button sets cancel_requested flag

Source: src/Tools/Common/GM/Shell/Services/CommitReviewWindowService.cs:68
Source: src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs:229
Source: src/Tools/Common/GM/Shell/ViewModels/CommitReviewViewModel.cs:489
Source: src/Tools/Common/GM/Shell/Views/CommitReviewWindow.xaml:111
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:4595


13.4 Row Locking

Individual rows are locked when their target is involved in another mapping.

Lock Sources:

  1. Families Tab: When a family row's nested mapping (Object Style, Material) is set to Apply, the corresponding row on the Object Styles or Materials tab is locked
  2. Duplicates Tab: When a duplicate mapping is accepted, the source row on the target tab is locked

Lock Behavior:

  • IsLocked = true on affected row
  • Apply checkbox disabled
  • Tooltip explains lock source: "Locked by Families tab (change there)" or "Locked by Duplicates tab (change there)"

Implementation:

// ApplyRowLocks() at GmWindowViewModel.MappingInitAndInfrastructure.cs
private void ApplyRowLocks()
{
    // Families-derived locks
    // Duplicates-derived locks
    // Sets IsLocked and LockTooltip on individual rows
}

Critical: Locking is ROW-level only. There is no tab-level locking.


13.5 Error Handling

Typed Exceptions:

  • GmInvalidMappingError — Invalid plan operations
  • GmApplyStageError — Execution failures with stage name and inner failures

Error Surfaces:

  • Commit Review shows validation errors before commit
  • Progress overlay shows execution errors with details
  • Notification banner summarizes outcome

Fail-Fast:

  • First error aborts transaction
  • User sees error message with option to retry or cancel
  • Partial changes rolled back

14. Performance

14.1 Virtualization

Requirement:

  • DataGrids use virtualization for large row counts (1000+ rows)
  • TextBlock (read-only) shown by default while scrolling
  • Switches to ComboBox when cell enters edit mode

Rationale:

  • Avoids UI hangs during scrolling with large datasets
  • Pre-building comboboxes for all rows is prohibitively expensive

Example (WPF):

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <TextBlock Text="{Binding TargetName}" />
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <ComboBox ItemsSource="{Binding TargetOptions}" ... />
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

14.2 Pre-building Target Options

Policy:

Pre-built:

  • Top-level target option lists for current category only (fast startup priority, <1000 families)
  • Families tab: when category selected, pre-build options for all families in that category
  • Materials/Styles tabs: pre-build on first visit

NOT Pre-built:

  • Nested detail options (Types, Parameters, Styles within expanded family rows)
  • Other curated categories (lazy load on category change)

Rationale:

  • Balances startup time with responsiveness
  • Pre-building ALL categories would be memory-intensive and slow

Background Pre-warming:

  • Background pre-warming on idle thread is not feasible due to Revit API UI thread constraint
  • First category switch warms cache; subsequent switches are instant
  • This is an accepted trade-off for correctness and architectural cleanliness

14.3 Lazy Loading Policy

Section Gating:

Families Section:

  • Built on first Families tab visit (or immediately if default tab)
  • Rebuilt on category change

Usage Section:

  • Built on first visit to Line Styles, Object Styles, or Materials tab
  • Cached for session (reused across these three tabs)
  • Style usage canonicalization normalizes GraphicsStyle/Category raw IDs to projection-style IDs per concrete category (no name-based cross-category collapse)
  • Canonicalization resolves raw category IDs (including built-in negative category IDs) before positive-ID filtering so valid object-style usage is not dropped.
  • Line/Object style scope classification uses ancestor-category matching so nested category chains still classify correctly

Shared Parameters Section:

  • Built on first visit to SP tab
  • Cached for session

Expanded Row Details:

  • Always lazy (never pre-built)
  • Build on "+" expand or "Scan Family" button
  • Nested target options built on target change

14.4 Option List Caching

Session Cache:

  • Target option lists cached in memory for session
  • Instant tab switching (no re-query)
  • Cleared on window close

Invalidation:

  • Category change: rebuild options for new category
  • Clear Scans: rebuild options affected by deep scan data

14.5 Visible-Row-Only Name Resolution

Strategy:

  • Resolve names for top N visible rows immediately (batch: 250 IDs)
  • Additional rows resolve as they scroll into view (if hooked to scroll event)
  • Or use id fallback text until explicit "Resolve All Names" action

Implementation:

// Batch resolution (limit 250)
src/Tools/Common/GM/Shell/UI/ViewModels/GmShellViewModel*.cs
  ResolveNamesAsync, EnsureNamesForIdsAsync
  RebuildMaterialStyleViews, RebuildTypeRows, RebuildFamilyList

// Scroll handlers
src/Tools/Common/GM/Shell/UI/Views/GmWindow.xaml(.cs)
  OnFamiliesScrollChanged, OnStylesScrollChanged, etc.

14.6 Throttling

Overlay Updates:

  • Progress steps tick at chunk boundaries
  • Throttled to ~200ms minimum between updates
  • Avoid UI spam

Threshold Debounce:

  • Threshold slider debounced 200ms
  • Avoid rebuilding options on every pixel drag

14.7 Usage Index Performance Contracts

Sampling Cap:

  • QuerySampleInstanceIdsBySymbol collects at most Constants.MaxUsageSamples (2500) instances
  • Model-owned instances sampled first (document-wide collector), then view-owned
  • One instance per FamilySymbolSymbolGeometry is shared across all instances of same type
  • Early exit when cap reached

Geometry Extraction:

  • Single get_Geometry() call per sampled instance
  • Options: ComputeReferences = false, IncludeNonVisibleObjects = true, View = ActiveView
  • No secondary "model geometry" pass — IncludeNonVisibleObjects captures all subcategory refs

Style Canonicalization:

  • BuildStyleCanonicalizationMap runs ONCE per usage build (not per chunk)
  • Simple mapping: raw GraphicsStyle/Category ID → canonical projection GraphicsStyle ID
  • Recorder/classifier preserve raw + canonical IDs only (no broad alias fan-out)

Deep Scan Isolation:

  • BuildStyleUsageAsync NEVER triggers deep scan
  • Deep scan is manual only (Section 15.1)
  • Families with zero placed instances have no usage data until manually scanned

Parameter Scanning:

  • FamilyScanner.QueryMappableParamNamesForFamily scans ONE instance per family
  • Parameter definitions are family-level — all instances share identical definitions
  • Type parameters from FamilySymbol objects; instance parameters from single sample instance

Part IV: Features

15. Deep Scan

15.1 Trigger Policy

Manual Only:

  • Never auto-triggered
  • User clicks "Scan Family" button or "Deep Scan" from context menu/toolbar
    • Deep scan cache hydration is separate: GM may restore previously-scanned results from cache at startup without opening the family.

Rationale:

  • Deep scan opens family in background (expensive operation)
  • User should control when this happens

15.2 Execution Flow

Steps:

  1. Show overlay: "Preparing scan..."
  2. Collect host-side scan data (no family open yet):
    • Parameter names (from loaded symbols + sample instances when available)
    • Sample instance IDs + per-type instance counts (bounded)
  3. Open family via EditFamily (Revit API call) and collect deep usage data:
    • Portable usage identifiers (cache-safe across documents):
      • Materials by name
      • Object Styles by GraphicsStyleKey (category-path key)
      • Nested families by family name
    • In-session host element IDs (where resolvable) for kernel/UI use
  4. Close family document
  5. Update kernel GMDeepScanState
  6. Write to deep-scan-cache.json cache file (mid-session write)
  7. Update UI: badges, details unlock, augment usage lists

Cancellation:

  • User can cancel during scan
  • Partial results discarded

15.3 Cache Persistence

File: deep-scan-cache.json (global, NOT document-specific)

Key (Composite):

{
  "FamilyName": "Wall-Basic",
  "CategoryId": -2000011,
  "TypeCount": 5,
  "ParameterSignature": "abc123def456"
}

Payload:

{
  "ParameterNames": ["Width", "Height", "Material", ...],
  "StyleKeys": [{ "ParentBuiltInCategoryId": -2000011, "ParentName": null, "Name": "Thin Lines" }, ...],
  "MaterialNames": ["Concrete", "Steel", ...],
  "NestedFamilyNames": ["Nested A", "Nested B", ...],
  "CachedAt": "2025-11-21T10:30:00Z"
}

Write Timing:

  • Immediately on scan completion (mid-session write)

Cross-Session Reuse:

  • Name-based keys enable global cache
  • Different documents can share deep scan data for same family

15.4 Cache Reuse Policy (Strict Restore)

Deep scan cache does not display a separate staleness badge. Instead, cache reuse is conservative:

  • Cache key mismatch (FamilyName/CategoryId/TypeCount/ParameterSignature) → cache miss → deep scan runs when user requests it
  • Cache restore is strict: if any cached portable identifier (style/material/nesting) is missing or ambiguous in the current document → cache miss
  • Cache restore is attempted as a startup hydration for eligible 0-instance, unscanned families. If strict restore rejects cached data, GM logs a warning and keeps the family unscanned until the user deep scans it.
  • Clicking Scan Family always performs a fresh deep scan and overwrites any existing cache entry.

Source: src/Tools/Common/GM/Shell/Services/DeepScanOrchestrationService.cs:128
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.MappingInitAndInfrastructure.cs:1144


15.5 Badge Indicators

DS Icon:

  • Shown on top-level family row when deep scan completed
  • Tooltip: "Deep scanned on {date}"

15.6 Augmentation of Usage Lists

Families discovered ONLY via deep scan (no instances in project) appear in:

  • "Families Using This Object Style" lists
  • "Families Using This Material" lists

Styling:

  • DS badge distinguishes deep-scan-only families
  • Usage count remains 0 (not inflated)

16. Type Mapping

16.1 Create New Type

UI:

  • Per-row Create checkbox in the expanded-family Types grid
  • Per-row New Type Name inline editor
  • No separate header-level create button (single creation flow)

Behavior:

  1. User checks Create on a type row.
  2. If New Type Name is empty, GM seeds it from the row's source type label.
  3. Type row Apply is derived automatically from either:
    • a valid selected target type, or
    • Create=true with a non-empty NewTypeName.
  4. Clearing Create clears NewTypeName and removes pending create-type intent from the live plan.

16.2 Inline Rename

UI:

  • New Type Name TextBox on each type row (enabled only when Create=true)

Behavior:

  1. User edits the inline name value.
  2. Name is stored in MatchingRow.NewTypeName.
  3. Planning emits CreateType operations from live-plan CreateTypes entries.

Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:1812
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1182
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1196
Source: src/Tools/Common/GM/Shell/Services/LivePlanState.cs:127
Source: src/Tools/Common/GM/Shell/Services/PlanBuildingService.cs:136


16.3 Plan/Apply/Review Integration

Planning:

  • Live-plan CreateTypes entries (SourceId, NewName) drive create operations
  • Operation kind: OperationKind.CreateType

Apply:

  • Transaction: new Transaction(doc, "Create Type")
  • FamilySymbol.Duplicate(name) → returns ElementId
  • Must call doc.GetElement(id) to get symbol

Review:

  • Created types shown in Commit Review with "New" indicator
  • Count included in summary: "5 types created"

17. Preview Pane

17.1 Toggle Behavior

Within expanded family details:

  • Selecting Object Style row → show preview (swatch + style line)
  • Selecting Material row → show preview (color swatch + texture)

Toggle:

  • Checkbox or auto-show on selection
  • Positioned on right side of window

17.2 Preview Content

Object Styles:

  • Swatch (color/pattern)
  • Line weight
  • Line pattern
  • Sample line rendering

Materials:

  • Color swatch
  • Texture preview (if applicable)
  • Shininess, transparency properties
  • Sample material rendering

18. Sorting, Filtering, Threshold

18.1 Similarity Mode

Visual Styling:

  • Colored labels: Green (high similarity) → Orange (medium) → Red (low)
  • Percentages appended: "Target Name (92%)"
  • Sorted high → low by score

Threshold Behavior:

  • Options below threshold NOT auto-selected
  • Manual selections NEVER overridden
  • Lowering threshold can clear auto-matches (NOT manual selections)

Applies To:

  • ALL target comboboxes (top-level and nested)
  • Families, Line Styles, Object Styles, Materials tabs

18.2 Alphabetical Mode

Visual Styling:

  • Default styling (no colored labels)
  • NO percentages appended
  • Sorted lexicographically A-Z

Threshold Behavior:

  • Threshold slider has NO effect (ignored)
  • All options visible regardless of score

Applies To:

  • ALL target comboboxes (top-level and nested)

18.3 Manual Selection Preservation

Critical Rule:

Manual selections are sticky across:

  • Sort mode changes (Similarity ↔ Alphabetical)
  • Threshold changes
  • Refresh operations

Only Reset If:

  • Target disappears from options (e.g., family deleted, category changed)

Visual Marking:

  • Manual selections visually distinguished (bold or icon)
  • Auto-selections use default styling

18.4 Nested Target List Behaviors

Nested target comboboxes (Types, Parameters, Styles, Materials within expanded family rows) must:

  1. Respect Sort Mode:

    • Similarity: colored labels, percentages, high→low
    • Alphabetical: default styling, A-Z, no scores
  2. Respect Threshold:

    • Similarity mode: threshold affects default selection
    • Alphabetical mode: threshold ignored
    • For nested Types rows, auto-selected defaults below threshold clear to a blank sentinel (no selection)
    • <Do Not Map> remains the first visible dropdown option and is only set by explicit user selection
  3. Preserve User Selections:

    • Manual, saved-mapping, and externally-originated selections are sticky across sort/threshold changes
  4. Update on Target Change:

    • When top-level target family changes on an expanded family row, rebuild nested options/defaults from the new target
    • Rebuild applies to Types, Parameters, Object Styles, and Materials nested lists
    • Auto-selected rows are recomputed; manual/saved/external rows are preserved

Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:1094
Source: src/Tools/Common/GM/Shell/ViewModels/FamiliesPaneViewModel.cs:1126
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.UsageAndFamilies.cs:388
Source: src/Tools/Common/GM/Shell/ViewModels/ShellViewModel.CoreAndCommands.cs:1196
Source: src/Tools/Common/GM/Shell/Views/GmShellWindow.xaml:110


Part V: Quality & Implementation

19. Known Issues & Roadmap

19.1 Completed Features

The following features are fully implemented:

  1. Families Parameter Pruning — Programmatic filters in RevitCompat.IsUserMappable
  2. Nested Target Comboboxes — Four nested collection types (Types, Parameters, Materials, Object Styles)
  3. Unscanned Family DemotionMatchingOption.Separator with greyed-out styling
  4. Deep-Scan Augmentation of Usage Lists — DS-only families merged with "(DS)" badge
  5. Cross-Tab Apply LockApplyRowLocks() with IsLocked property
  6. Nested Type Create Flow — per-row CreateNewType + NewTypeName pipeline (single UI path)
  7. Parameter Mapping <Do Not Map> SentinelMatchingOption.DoNotMap as first option
  8. Nested List Sorting/Threshold — Debounced refresh with selection preservation
  9. Deep Scan Overlay Bug Fix!IsBusy check prevents double overlay

19.2 Remaining Implementation Tasks

HIGH PRIORITY:

Deep Scan Global Cache

  • Status: Implemented — global deep-scan-cache.json file with portable payload and strict restore policy
  • Impact: Deep scan results can be reused across sessions and across documents when portable identifiers resolve cleanly
  • Implementation files:
    • src/Tools/Common/GM/Features/DeepScan/DeepScanCache.cs
    • src/Tools/Common/GM/Features/Caching/DeepScanCacheStrategy.cs
    • src/Tools/Common/GM/Shell/Services/DeepScanOrchestrationService.cs

Line Styles Tab Stability

  • Issue: Selecting Line Styles can error and close window
  • Likely causes: SelectionChanged executing before initial load, missing descriptor, VM contract violations
  • Tasks:
    1. Gate SelectionChanged until _initial_load_complete AND sender is real row
    2. Enforce VM contract post-enrichment; surface failures via overlay
    3. Validate descriptor presence at collection time

MEDIUM PRIORITY:

Preview Pane Toggle

  • Status: Preview panels exist but are always visible when data available
  • Task: Add user-controlled toggle button/checkbox in family details

SP Grid Empty State

  • Status: Generic "wrong category" banner exists
  • Task: Add specific "No shared parameters" banner

Broad Exception Cleanup

  • Task: Remove or narrow broad try/except to typed, narrow boundaries
  • Task: Verify no residual legacy module references

LOW PRIORITY:

Families Details Linking

  • Status: Implemented (parent target change now refreshes nested target lists for expanded rows)

Nested Threshold Verification

  • Status: Documented (nested target comboboxes follow global sort/threshold semantics)

19.3 Implementation Roadmap

Phase 1: Foundation

  1. Add Application-layer query wrappers
  2. Refactor PlanningOrchestrator to accept IGmPlanningService dependency
  3. Repository hardening: Add SnapshotVersion, strict parsing

Phase 2: UI Refactor

  1. UI refactor (GmWindowViewModel):
    • Constructor: accept only Application services
    • Remove adapter/service deps
    • Use kernel for families, usage, styles, materials
  2. Report window: consume IMappingExecutionService from main VM

Phase 3: DI and Cleanup

  1. DI consolidation in shell: compose adapters → Application services → VM
  2. Clean deletions: remove UI callsites referencing Infrastructure/Services

Phase 4: Validation

  1. Build solution; verify no UI project references Infrastructure namespaces
  2. Manual sanity pass: Families tab, Usage tabs, Deep scan, SP authoring, Mapping report

20. Quality Acceptance Criteria

20.1 Perfection Standards

Core Philosophy:

"Deliver a near-perfect, clean, spec-compliant GM codebase that we could ship to 1,000,000,000 users with confidence."

What "Perfection" Means:

  1. Spec → Code Traceability: Every clause maps to concrete code
  2. One Way to Do a Thing: Canonical, testable paths (no duplicate logic)
  3. Clean Layering: Collectors produce unique sets; controllers bind once
  4. Readable, Minimal, Modular: No dead blocks or drift
  5. Zero Tolerance for Silent Failures: All errors surfaced explicitly

20.2 Green Checks Only

Acceptance Policy:

ALL of the following must be true before merge:

  1. All tests pass (no warnings)
  2. No duplicate curated lists outside single source
  3. No duplicate top-level rows
  4. Revisit any tab → no overlay reload
  5. Sort-mode switch updates Display/brush across all visible rows
  6. Families details: Types/OS/Materials/Parameter Names without DS when Qty>0
  7. SP tab: group default = first alphabetical; category = first curated

20.3 Behavioral Validation

Manual Testing Scenarios:

  1. Kernel Determinism:

    • Given same Revit document and filters → two builds produce identical kernel
    • Changing category filter only replaces Families (preserves Usage)
  2. No Duplicate Reads Per Tab:

    • Navigate between tabs → verify no additional adapter calls beyond name previews
    • Use adapter call counters
  3. Mapping Planning Correctness:

    • Self-maps and zero/negative IDs rejected with GmInvalidMappingError
    • Parameter migrations execute during type replacements (adjacent to type swaps); operation results are recorded under ApplyStages.Parameters
  4. Placement Taxonomy:

    • Adapter placements normalize into { NotHosted, FaceBased, WorkPlaneBased } only
  5. Snapshot Round-Trip:

    • Save → close → reopen → verify UI state restored (category, filters, scroll, kernel snapshot)
    • Corrupt snapshot throws GmSnapshotLoadException, UI proceeds with fresh build
  6. Deep Scan UX Integration:

    • Run deep scan → badges update, availability changes, no usage indices re-read

END OF SPECIFICATION