diff --git a/docs/adr/0004-standard-inspection-surface.md b/docs/adr/0004-standard-inspection-surface.md index a654160..6237ddd 100644 --- a/docs/adr/0004-standard-inspection-surface.md +++ b/docs/adr/0004-standard-inspection-surface.md @@ -71,8 +71,8 @@ schemas for implemented operations. CommandKit supplies shared handlers with predictable meanings and discovers their availability from registration. Each enabled operation needs its own data -contract: comparison needs safe snapshots, catalogs need enumeration, and profile -display needs safe execution-target inspection. +contract: comparison needs supported native comparison results, catalogs need +enumeration, and profile display needs safe execution-target inspection. The [scope model](0009-configuration-scopes.md) and [comparison policy](0008-semantic-configuration-diff.md) describe related diff --git a/docs/adr/0005-invocation-outcomes.md b/docs/adr/0005-invocation-outcomes.md index 392002b..9db3100 100644 --- a/docs/adr/0005-invocation-outcomes.md +++ b/docs/adr/0005-invocation-outcomes.md @@ -55,7 +55,7 @@ A writer failure can leave a partial report and returns operational failure. The outcome identifies incomplete delivery. Custom handler output remains application-owned. [ADR 0007](0007-native-yaml-reports.md) covers `config show` report preparation; [ADR 0008](0008-semantic-configuration-diff.md) defines successful -comparison, including differences and coverage gaps. +comparison outcomes. ## Alternatives considered diff --git a/docs/adr/0007-native-yaml-reports.md b/docs/adr/0007-native-yaml-reports.md index 1ce566f..e0a8e2f 100644 --- a/docs/adr/0007-native-yaml-reports.md +++ b/docs/adr/0007-native-yaml-reports.md @@ -68,6 +68,6 @@ allows a failure in one scope to prevent combined display. Explicit single-scope display remains available for a narrower view. Additional structured schemas are compatibility contracts owned by CommandKit. -Native concatenation can use existing writers; semantic comparison separately -requires the safe structured data described in +Native concatenation can use existing writers; semantic comparison uses the owning +library's supported comparison API under [ADR 0008](0008-semantic-configuration-diff.md). diff --git a/docs/adr/0008-semantic-configuration-diff.md b/docs/adr/0008-semantic-configuration-diff.md index 77e9004..f91fdc8 100644 --- a/docs/adr/0008-semantic-configuration-diff.md +++ b/docs/adr/0008-semantic-configuration-diff.md @@ -1,4 +1,4 @@ -# ADR 0008: Compare semantic snapshots with explicit sensitivity gaps +# ADR 0008: Delegate effective configuration comparison to owning libraries ## Status @@ -6,72 +6,79 @@ Accepted. ## Date -2026-10-06 +2026-10-06; revised 2026-10-11. ## Context -Configuration comparison helps operators understand changes in final effective -values across roots and overlays. Formatting, authored file layout, and provenance -identifiers are different properties from effective-value equality. Sensitivity -classification also limits the values available for inspection. - -Meaningful comparison requires structured effective snapshots with per-path -sensitivity data from the owning libraries. The comparison policy must preserve -value semantics while making limited coverage visible to operators. +ConfigKit provides a native configuration comparison capability. Exposing it +through CommandKit gives applications a consistent operator interface and reduces +repeated command, selection, and reporting infrastructure. ConfigKit remains the +authority for the meaning and disclosure of its comparison results. ## Decision -For a scope declaring safe comparison support, `config diff` compares two -independently resolved effective configurations. CommandKit checks that capability -before acquisition. Providers supply captured results through the owning -libraries' supported public inspection APIs. +### Comparison ownership -Compare values using the owning library's safe structured inspection semantics, -including defaults and recorded domain resolution. Preserve the distinctions -among absent values, null, empty mappings, and empty sequences. Mappings are -compared recursively; ordered sequences are compared as whole values. +For ConfigKit-backed scopes declaring comparison support, `config diff` exposes +ConfigKit's native comparison API. CommandKit checks capability availability before +acquisition and coordinates selection, acquisition, adaptation, presentation, +and invocation outcomes. -For each path, use the union of the two sides' sensitivity classifications to -protect both before and after presentation. A value classified on either side -receives the corresponding safe treatment in both snapshots. Hidden values -produce explicit comparison gaps with their paths and supported reasons. +Integrations delegate comparison to their owning libraries' supported public +APIs. ConfigKit owns its comparison semantics and disclosure rules. CommandKit +follows the public contract of the ConfigKit version it adopts. When an adopted +upstream revision changes those semantics, the changes flow through to +CommandKit's integration. Upstream remains authoritative as the capability evolves. -Equality conclusions cover the comparable data. A comparison with gaps reports -its limited coverage alongside observed differences. A complete comparison with -an empty difference set supports an equality conclusion for the inspected -selection. Redaction masks are presentation data; classification metadata -determines comparison coverage. +Application-supplied providers retain ownership of their implementation and +loading effects. [ADR 0012](0012-typed-configuration-comparison-pairs.md) governs +paired selections, provider contracts, and typed input/shared-state lifetimes. -Text and YAML reports include differences and coverage gaps. The exact report -schema belongs in the [CLI reference](../cli.md) when implemented. Selection -syntax, inheritance rules, and detailed change records are outside the scope -of this decision. +### Safe disclosure and presentation -A completed comparison returns success whether it finds differences or gaps; -load, comparison, and writer failures use the operational failure classification in -[ADR 0005](0005-invocation-outcomes.md). +Adapt and present native comparison results in accordance with the adopted +upstream API's disclosure contract. The integration carries the upstream results +and relevant metadata through its public provider boundary. + +CommandKit owns report presentation and may summarize or transform the returned +safe comparison data for different operator needs. Reports may vary in format, +organization, and detail while respecting safe disclosure and the evidence +provided by the comparison. Each report's documented meaning defines the +conclusions an operator can draw from it. + +Report formats, schemas, and rendering choices belong in the +[CLI reference](../cli.md) when implemented. They can evolve independently of +this ownership decision under the [release policy](../policy/release.md). + +### Invocation outcomes + +A completed comparison returns success regardless of whether differences are +reported. Load, comparison, and writer +failures use the operational failure classification in +[ADR 0005](0005-invocation-outcomes.md). Applications may use comparison results +to make their own domain decisions. ## Alternatives considered | Alternative | Analysis | | --- | --- | -| Rendered YAML line comparison | Reuses native display output. Formatting and masking become part of change interpretation. | -| Compare masks as literal strings | Uses visible snapshot bytes directly. Equal masks express equal presentation while hidden values remain uncertain. | -| Use one side's sensitivity policy | Simplifies classification selection. A value's disclosure treatment depends on which snapshot supplies that policy. | -| Fetch secret values for comparison | Gives access to additional equality evidence. Comparison acquires sensitive material and additional confidentiality responsibilities. | -| Element-by-element sequence diff | Gives detailed collection changes. Stable element identity and alignment need another comparison contract. | -| A distinct status for observed differences | Supports assertion-oriented scripts. Inspection completion and change detection share an outcome signal. | +| Compare safe snapshots in CommandKit | Gives CommandKit direct control over comparison, but requires it to maintain equality and sensitivity logic and account for values already withheld from snapshots. Native comparison keeps those responsibilities with the owning library. | +| Compare rendered configuration text | Reuses existing output, but formatting and masking affect the apparent differences. Native comparison supplies evidence grounded in the library's effective-value model. | +| Leave comparison commands entirely to applications | Preserves application flexibility, but repeats selection, acquisition coordination, and presentation infrastructure across consumers. Integration providers retain application control within the common command system. | +| Return a distinct status when differences are found | Supports assertion-oriented scripts, but couples successful inspection with a policy judgment about the result. The selected outcome model leaves that judgment to consumers. | ## Consequences -Operators receive meaningful effective-value changes with explicit uncertainty -where sensitivity limits comparison. Whole-sequence comparison keeps -implementation scope focused at the cost of less detailed collection changes. +CommandKit supplies consistent comparison commands while relying on owning +libraries for comparison semantics and safe disclosure. Dependency adoption +includes adapting the integration and its documentation to upstream changes. +New library integrations +are assessed through their supported comparison contracts; each scope can expose +its available capabilities independently under +[ADR 0009](0009-configuration-scopes.md). -Diff support depends on safe snapshots preserving value semantics and sensitivity. -CommandKit applies this comparison policy to scopes declaring that capability. -Other operations and families can be integrated independently under -[ADR 0004](0004-standard-inspection-surface.md). - -Applications can use the successful report as input to domain decisions or future -assertion workflows. +Reports can serve different operator needs using the same native comparison +data. Their level of detail may differ, and sensitivity rules may limit what +can be shown. Maintaining the boundary between comparison evidence and report +presentation allows those reports to evolve while preserving library ownership +and consumer control. diff --git a/docs/adr/0011-captured-declarations-and-installation.md b/docs/adr/0011-captured-declarations-and-installation.md index ee11ced..d27fadd 100644 --- a/docs/adr/0011-captured-declarations-and-installation.md +++ b/docs/adr/0011-captured-declarations-and-installation.md @@ -9,148 +9,140 @@ input/state lifetimes. Partially supersedes consequence during the unreleased prototype transition, and refines its convenient composition principle. +Refined by [ADR 0012](0012-typed-configuration-comparison-pairs.md) for +two-selection configuration comparison and its input/state lifetime. The +single-selection lifecycle and other decisions remain in force. + ## Date 2026-10-10 ## Context -Optional integration modules need to assemble complete standard families through -public core contracts. Applications also declare their own commands and groups. -Different construction and installation paths would add repeated error checks -and registration plumbing, weakening CommandKit's infrastructure-reduction goal. -The current implementation is an unreleased prototype with no downstream -consumers. Its declarations are evidence to evaluate, without compatibility -obligations that would constrain the selected API. +Applications declare domain commands, while optional integrations assemble +standard command families. A common construction and installation model reduces +repeated error checks and registration plumbing. Integrations need public typed +composition contracts to assemble those families while preserving core and +backend boundaries. -Declaring an optional integration is distinct from installing it. Broad references -to reserved standard families in ADR 0001 do not specify when an optional name -becomes reserved. Dependency selection alone should not consume an application -namespace or install commands. +Declaration and installation have different responsibilities. An optional family +claims a namespace when installed; selecting a dependency alone should leave the +application's command surface unchanged. During execution, scope-specific CLI +inputs and application-owned shared state also have distinct lifetimes. Making +those lifetimes explicit avoids accidental aliasing between selected scopes. -Configuration inspection can select several scopes. Those scopes may share an -application-owned cache while receiving different selector values. Combining -selector storage and shared state in one context obscures their distinct -lifetimes and can cause scope-specific inputs to alias. Separate input and state -types make those lifetimes explicit. -Integrations also need to compose typed bindings without accessing private core -parsers and setters or exposing backend structures. +At the time of this decision, the implementation was an unreleased prototype +with no downstream consumers. Its API shapes carried no compatibility obligations +that would constrain the selected design. ## Decision -### Uniform captured declarations +### Declaration capture and reuse Applications and integrations construct the same opaque core command declarations and install them through core application construction or registration. Initial -integrations provide complete-family factories and focused conveniences; they do -not add separate integration-specific installation APIs. Suite/provider types -remain integration-owned rather than becoming a universal core provider model. +integrations provide complete-family factories and focused conveniences through +that common installation API. Suite and provider types remain integration-owned. -Factories capture metadata, bindings, capability selections, selector -applicability, and callback references eagerly. They copy the declaration -collections needed so later edits to the source metadata do not change a returned -command. Construction errors remain attached to the declaration and are reported -at installation. There is no public deferred preparation lifecycle or untyped -integration payload. +Factories eagerly capture metadata, bindings, capability selections, selector +applicability, and callback references. They copy the declaration collections +needed to isolate returned commands from later edits to source metadata. +Construction errors remain attached for installation to report. This model uses +eager declarations with public typed composition; a deferred preparation lifecycle +and untyped integration payload are outside the selected API. -Core installation validates the whole batch before registry mutation, including +Declarations may be reused across applications with independent registry and +invocation state. Callback references and captured application objects retain +caller ownership. Reuse provides no deep-copy or concurrent-dispatch guarantee. +Application helpers performing other fallible work may still return errors. + +### Installation and retained errors + +Core validates each installation batch before registry mutation, checking retained errors, bindings, placement, and collisions. Failed application -construction returns no usable application; failed later registration leaves the -existing registry intact. This atomicity applies to one installation call, not -separate calls or arbitrary effects in caller code. Report retained errors with -useful command context and preserve supported wrapped error identity without -requiring a particular aggregate-error representation. +construction returns no usable application; failed later registration preserves +the existing registry. Atomicity covers one installation call. Separate calls +and arbitrary callback effects retain their own lifetimes. -An explicit absence declaration contributes no command, help entry, or namespace -claim. Installation checks its attached errors before skipping it. A zero command -declaration remains invalid. Integrations define their own empty-suite semantics: -the initial configuration suite needs at least one named scope, while a valid -definition suite with no capabilities is absent. Metadata-only configuration -scopes remain discoverable. A nil loader supplied to an adapter is invalid wiring; -narrowing its capability list does not erase that error. +Report retained errors with useful command context and preserve supported wrapped +error identity. The aggregate-error representation remains an implementation +choice. -Declarations may be installed in multiple applications with independent registry -and invocation state. Callback references and the objects they capture remain -application-owned; declaration reuse neither deep-copies those objects nor -establishes concurrent-dispatch support. Application helpers performing other -fallible work may still return errors. +An explicit absence declaration contributes no command, help entry, or namespace. +Installation checks attached errors before skipping it. A zero command declaration +remains invalid. Integrations define their own empty-suite semantics: the initial +configuration suite needs at least one named scope, and metadata-only scopes +remain discoverable. A definition suite with no capabilities contributes nothing. -### Optional-family namespace lifetime and placement +Binding validation applies to generated command declarations. Unused selector +bindings in an absent family are ignored; explicitly retained adapter-construction +errors still reach installation. A nil loader supplied to an adapter remains +invalid wiring even if its capabilities are subsequently cleared. -Core construction always reserves help and version. An optional standard family -owns its complete root namespace when a real family declaration is installed. -An import or absent declaration reserves nothing. An application may use that -name while the family is uninstalled, but later installation then fails with a -collision. Application registration after standard-family installation fails in -the same way. Neither order merges groups, replaces handlers, or permits private -application operations beneath an installed standard family. +### Namespace ownership and placement -Standard-family declarations carry root-only placement metadata. Core rejects -their installation below another group. Placement is a public declaration -property, not authentication of the declaring package or a core allowlist of -integration names. A real empty root-only group still claims its namespace and -obeys placement checks; explicit absence does neither. +Core construction reserves help and version. An optional standard family owns +its complete root namespace upon installation. Imports and absent declarations +claim nothing. An application may occupy an uninstalled family name; installing +a conflicting declaration then fails in either registration order. Installation +preserves existing handlers and group ownership. Private application operations +remain outside installed standard families. -### Separate typed input from shared acquisition state +Standard families carry root-only placement metadata, which core enforces when +they are nested beneath another group. Placement is a public declaration property; +its enforcement is independent of package identity or a list of integration names. +A real empty root-only group claims its namespace and obeys placement checks. +Explicit absence contributes no node to place. + +### Input and shared-state lifetimes Integration suites distinguish typed CLI input from typed shared acquisition -state. Configuration inspection initializes and populates one input per selected -scope, applying only applicable selector bindings, and initializes shared state -once for the selected inspection invocation. Named definition inspection uses -one input and one shared state. Providers receive both explicitly. -Configuration input validation receives the selected scope identity together -with its applicable typed input. +state. Ordinary configuration inspection initializes one input per selected scope, +applies its applicable selector bindings, and initializes shared state once for +the inspection invocation. Input validation receives the scope identity and its +typed input. Named definition inspection uses one input and one shared state. +Providers receive both explicitly. ADR 0012 refines this lifecycle for comparison +pairs with two selections of one scope. Availability preflight and applicable input checks precede shared-state -initialization and acquisition. A failed check stops that path; later input -factories need not run. Construction, installation, help, version, and scope -discovery invoke neither input/state factories nor acquisition providers. +initialization and acquisition. Failures may stop later input factories. +Construction, installation, help, version, and scope discovery leave suite +input/state factories and acquisition providers uninvoked. -An omitted factory supplies its type's zero value, including nil for a pointer. -Applications supply initialization when their accessors or providers need it. -CommandKit does not allocate arbitrary pointer types, clone arbitrary application -graphs, repair aliases returned by factories, or reject a provider that accepts -nil intentionally. Core-owned parsed collections remain independent when applied -to separate inputs. Caller-owned objects and callback effects retain their own -lifetimes. +An omitted factory supplies its type's zero value, including nil pointers. +Applications provide initialization needed by their accessors and providers; +providers may intentionally accept nil. Allocation of application objects, +cloning, and aliases returned by factories remain caller responsibilities. +Core-owned parsed collections stay independent when applied to separate inputs. +Caller-owned objects and callback effects retain their own lifetimes. -Provide a narrow typed core binding-composition contract to capture converted -values and apply a selected binding subset to an initialized input. Preserve -binding identity, defaults, and presence without string-based handler lookup or -exposure of private setters, parser state, or backend objects. Copying a binding -preserves its identity; constructing a lookalike binding does not. +### Typed binding composition -Execution-required checks apply to the selected subset, so an app-only selector -does not become required for an engine-only scope request. Missing required input -is a usage failure. An invalid binding selection or destination is a wiring -failure, classified as operational when encountered at invocation. Those -categories remain identifiable through supported wrapping; assembly-detectable -metadata defects remain installation errors. Both invocation failures stop before -shared-state initialization and acquisition. This composition is part of the -existing typed-input model, not another parser or a raw-argument handler API. +Core provides a narrow typed contract to capture converted values and apply a +selected binding subset to initialized input. It preserves binding identity, +defaults, and presence while keeping private setters, parser state, and backend +objects behind their existing boundaries. A copied binding retains its identity; +a newly constructed lookalike has its own identity. + +Execution-required checks apply to the selected subset. Missing required input +is a usage failure; invalid binding selection or destination is a wiring failure +with operational classification at invocation. These categories remain identifiable +through supported wrapping and stop execution before shared-state initialization +and acquisition. Metadata defects detectable during assembly of generated +commands remain installation errors under the validation boundary above. ### Relationship to earlier decisions -No compatibility wrappers or downstream migration guide are required for this -unreleased prototype transition. This replaces -ADR 0010's consumer-migration consequence for this transition only. Changes to -published contracts remain governed by the [release policy](../policy/release.md). +For the unreleased prototype transition covered by this decision, compatibility +wrappers and a downstream migration guide were unnecessary. This exception to +ADR 0010's migration consequence is limited to that transition. Changes to +published contracts follow the [release policy](../policy/release.md). -ADR 0001 continues to govern dispatcher ownership, explicit instance registries, -typed bindings, presence, and application/domain boundaries. This decision -clarifies that optional-family namespace ownership begins upon installation. -It refines the construction and installation mechanics left open there and -distinguishes integration input from shared state. -It does not change the grammar, report formats, supported operation inventory, -or outcome/delivery rules in ADRs 0004 through 0009. - -ADR 0010 continues to govern separate core/integration module contracts and -dependency direction. Integration factories return core declarations through -supported public composition contracts. Deliberate exports, private -implementation state, and backend isolation remain governed by -[ADR 0003](0003-public-facade.md) as scoped by ADR 0010. Exact exported signatures -belong in Go declarations and GoDoc when implemented; this record owns the -architectural choices rather than an implementation inventory. +ADR 0001 retains dispatcher, registry, typed-input, and application-ownership +principles. ADR 0010 governs module boundaries and dependency direction, with +[ADR 0003](0003-public-facade.md) governing deliberate exports and backend isolation +within that scope. ADRs 0004 through 0009 retain their operation, grammar, report, +and outcome decisions. Go declarations and GoDoc own exact implemented signatures. ## Alternatives considered @@ -166,22 +158,15 @@ architectural choices rather than an implementation inventory. ## Consequences -Consumers assemble domain commands and optional families visibly through one -installation boundary. Integrations remain convenient without private core -access. Errors identified during eager assembly are observed at installation, -before provider acquisition. +Consumers assemble domain commands and optional families through one installation +boundary. Eager assembly errors reach that boundary before provider acquisition. +Namespace collisions remain explicit choices for the application to resolve. -Installing an optional family can fail if the application already uses its root -name; the application decides which declaration to install. Installed standard -families retain their ownership and root placement in either registration order. +Separate input and state types make initialization and sharing visible. Focused +conveniences support simple applications, while typed composition gives +integrations the public contracts needed to assemble their families. -Composed suites use two types and explicit initialization when needed. The -context-free application-configuration convenience hides unused suite machinery -for simple consumers. The public typed-composition seam adds a deliberate core -contract while keeping implementation and backend state private. - -The [architecture policy](../policy/architecture.md) owns normative boundaries. -Implementation status and sequencing remain in the roadmaps. These choices -govern CommandKit's declarations and calls; application loading, cache design, -startup, domain validation, callback effects, and process exit remain consumer -choices. +The [architecture policy](../policy/architecture.md) owns normative boundaries; +roadmaps own implementation status and sequencing. Application loading, cache +design, startup, domain validation, callback effects, and process exit remain +consumer responsibilities. diff --git a/docs/adr/0012-typed-configuration-comparison-pairs.md b/docs/adr/0012-typed-configuration-comparison-pairs.md new file mode 100644 index 0000000..f8e9732 --- /dev/null +++ b/docs/adr/0012-typed-configuration-comparison-pairs.md @@ -0,0 +1,108 @@ +# ADR 0012: Prepare typed selection pairs for configuration comparison + +## Status + +Accepted. Refines [ADR 0011](0011-captured-declarations-and-installation.md) for +two-selection configuration comparison; its other decisions remain in force. +[ADR 0008](0008-semantic-configuration-diff.md) governs native comparison and +report presentation. + +## Date + +2026-10-11 + +## Context + +Native effective-configuration comparison needs two completed loads. ConfigKit +requires results of the same configuration type, which an ordinary inspection +view does not expose for pairwise comparison. A generic adapter can retain that +type without introducing result assertions or a universal core provider model. + +ADR 0011 separates a selected scope's typed input from shared acquisition state. +A comparison selects the same scope twice, potentially with different roots or +overlays. Reusing one input obscures which selection a provider receives; creating +separate shared state loses the invocation-wide lifetime chosen for inspection. + +## Decision + +### Selections and provider ownership + +Comparison selects one scope and prepares an earlier/left selection and a +later/right selection. Omitted right selection fields inherit the corresponding +left field and supplied state. An explicit right override, including an empty +value, replaces that field. Providers retain interpretation of omitted defaults +and explicit empty values. Applicable domain selector values are shared between +the two sides initially; independent per-side domain selectors may be considered +for a concrete consumer need. + +Equal selections are allowed. CommandKit does not skip acquisition merely because +their selection values are equal: caller-owned loading may produce different +effective results. Exact flag spellings belong in the CLI reference when +implemented. + +Use a narrow optional comparison provider owned by the integration. Its request +groups each side's selection with its typed input and supplies one shared state +value separately. The provider returns acquired native comparison results +with diagnostics and an error through the existing acquisition convention. +Reuse useful upstream public record types rather than duplicating them solely +to rename fields. Exact exported signatures belong in Go declarations and GoDoc. + +The typed ConfigKit adapter invokes the supplied loader for each reached side +and passes the successful typed results to `configkit.Diff`. It retains the +configuration type inside that adapter, without reflection, result type +assertions, private core access, or a comparison-facet framework. Applications +may supply their own pair provider through the same integration contract. + +Declared capabilities determine required wiring. A comparison-only scope needs +its comparison provider, without an unrelated ordinary acquisition callback. +Ordinary operations continue to use their ordinary acquisition provider. +Retained adapter construction errors still apply when capabilities are narrowed. +Other scopes and families need not support comparison under +[ADR 0009](0009-configuration-scopes.md). + +### Typed input and shared state lifetime + +Capture comparison declarations and applicable selector bindings under ADR 0011. +Convert supplied flag values through core once, including on help paths; applying +the values to both inputs does not parse them again. + +After availability preflight, initialize each reached side's typed input +separately, apply the same applicable captured selector values, and check that +input with the selected scope identity and that side's selection. Finish both +input checks before initializing shared state once and invoking the pair +provider. Failures may stop later factories or loads. Construction, installation, +help, version, and scope discovery invoke neither input/state factories nor +acquisition/comparison providers. + +Existing factory ownership rules remain unchanged: an omitted factory supplies +its type's zero value, including nil pointers. Separate factory calls do not +guarantee distinct caller-owned objects. CommandKit does not allocate arbitrary +pointer types, clone application graphs, repair caller aliases, or supply or +repair application caches. Core-owned parsed collections remain independent +when applied to the two inputs; callback effects remain caller-owned. + +## Alternatives considered + +| Alternative | Analysis | +| --- | --- | +| Symmetric independently resolved left/right selectors | Makes each side explicit and avoids inheritance. It adds vocabulary and repetition for common base-versus-overlay comparisons. | +| One typed input for both sides | Requires fewer factory calls, but obscures side-specific selection checks and leaves mutation isolation implicit. | +| Acquire a comparison facet with each ordinary view | Shares single-side acquisition across operations, but requires a public compatibility contract between heterogeneous facets and additional handle or assertion machinery. | +| Separate shared state for each side | Isolates acquisition state, but loses the invocation-wide sharing already selected for inspection. Applications can choose isolation within their own pair provider if needed. | + +## Consequences + +Pair preparation has explicit input and state lifetimes while ordinary inspection +keeps its existing lifecycle. Generic result handling stays inside the integration +adapter; core gains no owning-library dependency or universal comparison model. + +Shared state permits application-owned caches. A cache used by a pair provider +should distinguish selections and any input values affecting loading to avoid +reusing the left result for a different right selection. This is integration +guidance, not a CommandKit cache policy or guarantee about caller callbacks. + +The [architecture policy](../policy/architecture.md) owns normative boundaries. +ADR 0008 governs delegation to upstream comparison semantics, safe disclosure, +and CommandKit's presentation responsibility. This decision does not add a new +equality engine or strengthen application +loading, object ownership, or concurrency contracts.