Class: CommandTower::Configuration::Registry::ClientCompatibility::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/command_tower/configuration/registry/client_compatibility/config.rb

Overview

Two-source registry composing platform-global policy, named client contracts, capability-scoped entity requirements, and host bindings. Mirrors the principal_capabilities / admin_workspace / audit registry pattern (CT-owned vs host-owned, finalize!/freeze, reset_host_definitions! for specs). Authority §5-§9.

V1 ships with an empty CommandTower-owned catalog by design: no platform requires any contract or entity requirement out of the box.

Constant Summary collapse

PLATFORMS =
%i[web ios android].freeze
MODES =
%i[observe enforce].freeze
CONTRACT_ID =
/\A[a-z][a-z0-9_]*(\.[a-z0-9][a-z0-9_]*)*\z/
PLATFORM_CONTRACTS =

CommandTower-owned seed catalog. Deliberately empty for V1 — see authority §7.5. Do not seed placeholder/fictional contracts here.

[].freeze
PLATFORM_ENTITY_REQUIREMENTS =
{}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Config

Returns a new instance of Config.



34
35
36
37
38
39
40
41
42
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 34

def initialize
  @mode = :observe
  @platforms = {}
  @contracts = {}
  @entity_requirements = {}
  @bindings = {}
  @finalized = false
  seed_platform_catalog!
end

Instance Attribute Details

#mode ⇒ Object

Returns the value of attribute mode.



32
33
34
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 32

def mode
  @mode
end

Instance Method Details

#apply_env_overlay!(env: ENV) ⇒ Object

Process-level ENV overlays (never the client's own headers). COMMAND_TOWER_CLIENT_COMPATIBILITY_MODE may set observe/enforce. COMMAND_TOWER_CLIENT_COMPATIBILITY_MINIMUM_{WEB,IOS,ANDROID} may only RAISE an already-configured platform's minimum — never lower it, and never configure a platform the host did not enable.



206
207
208
209
210
211
212
213
214
215
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 206

def apply_env_overlay!(env: ENV)
  raise_if_finalized!

  overlay_mode = env["COMMAND_TOWER_CLIENT_COMPATIBILITY_MODE"]
  self.mode = overlay_mode if overlay_mode.present?

  PLATFORMS.each { |platform| apply_env_minimum_overlay!(platform, env:) }

  self
end

#bind(contract_id) {|definition| ... } ⇒ Object

Yields:

  • (definition)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 100

def bind(contract_id)
  raise_if_finalized!
  normalized = normalize_contract_id!(contract_id)
  unless @contracts.key?(normalized)
    raise CommandTower::ClientCompatibility::UnknownClientContractError,
      "cannot bind unknown client contract #{normalized}"
  end

  if @bindings.key?(normalized)
    raise CommandTower::ClientCompatibility::DuplicateBindingError,
      "client contract #{normalized} is already bound"
  end

  definition = BindingDefinition.new
  yield definition if block_given?
  definition.validate!(contract_id: normalized)
  @bindings[normalized] = definition
  definition
end

#binding_for(contract_id) ⇒ Object



120
121
122
123
124
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 120

def binding_for(contract_id)
  @bindings[normalize_contract_id!(contract_id)]
rescue CommandTower::ClientCompatibility::InvalidContractIdError
  nil
end

#client_contract(name, owner: :host) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 73

def client_contract(name, owner: :host)
  raise_if_finalized!
  normalized = normalize_contract_id!(name)
  existing = @contracts[normalized]
  if existing
    if existing.owner == :command_tower && owner.to_sym == :host
      raise CommandTower::ClientCompatibility::HostOverrideError,
        "host cannot redefine CommandTower-owned client contract #{normalized}"
    end

    raise CommandTower::ClientCompatibility::DuplicateContractError,
      "client contract #{normalized} is already registered"
  end

  definition = ContractDefinition.new
  definition.id = normalized
  definition.owner = owner.to_sym
  @contracts[normalized] = definition
  definition
end

#configured_platforms ⇒ Object



65
66
67
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 65

def configured_platforms
  @platforms.keys.dup.freeze
end

#contract_registered?(name) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 94

def contract_registered?(name)
  @contracts.key?(normalize_contract_id!(name))
rescue CommandTower::ClientCompatibility::InvalidContractIdError
  false
end

#entity(name, owner: :host) {|definition| ... } ⇒ Object

Yields:

  • (definition)


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 126

def entity(name, owner: :host)
  raise_if_finalized!
  normalized = normalize_entity_name!(name)
  existing = @entity_requirements[normalized]
  if existing
    if existing.owner == :command_tower && owner.to_sym == :host
      raise CommandTower::ClientCompatibility::HostOverrideError,
        "host cannot redefine CommandTower-owned client compatibility entity requirement #{normalized}"
    end

    raise CommandTower::ClientCompatibility::DuplicateEntityRequirementError,
      "client compatibility entity requirement #{normalized} is already registered"
  end

  definition = EntityRequirementDefinition.new
  yield definition if block_given?
  definition.owner = owner
  definition.validate_definition!(name: normalized)
  @entity_requirements[normalized] = definition
  definition
end

#entity_requirement(name) ⇒ Object



148
149
150
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 148

def entity_requirement(name)
  @entity_requirements[name.to_s]
end

#finalize! ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 168

def finalize!
  (@platforms.values + @entity_requirements.values + @bindings.values).each do |definition|
    next unless definition.respond_to?(:class_composer_freeze_objects!)

    definition.class_composer_freeze_objects!(behavior: :raise, children: true)
  end
  @platforms.freeze
  @contracts.freeze
  @entity_requirements.freeze
  @bindings.freeze
  @finalized = true
  self
end

#finalized? ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 182

def finalized?
  @finalized
end

#opted_in? ⇒ Boolean

Whether the host has meaningfully engaged this registry at all. A completely untouched registry (no platforms, no host contracts, no host entity requirements, no bindings, default observe mode) is inert: evaluation is a no-op and boot MUST NOT fail on it.

Returns:

  • (Boolean)


160
161
162
163
164
165
166
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 160

def opted_in?
  @mode == :enforce ||
    @platforms.any? ||
    @contracts.any? { |_id, definition| definition.owner == :host } ||
    @entity_requirements.any? { |_name, definition| definition.owner == :host } ||
    @bindings.any?
end

#platform(name) {|definition| ... } ⇒ Object

Yields:

  • (definition)


55
56
57
58
59
60
61
62
63
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 55

def platform(name)
  raise_if_finalized!
  key = normalize_platform!(name)
  definition = @platforms[key] || PlatformDefinition.new
  yield definition if block_given?
  definition.validate_definition!(platform: key)
  @platforms[key] = definition
  definition
end

#platform_policy(name) ⇒ Object



69
70
71
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 69

def platform_policy(name)
  @platforms[name.to_s.strip.downcase.to_sym]
end

#registered_entity_requirement?(name) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 152

def registered_entity_requirement?(name)
  @entity_requirements.key?(name.to_s)
end

#reset_host_definitions! ⇒ Object



217
218
219
220
221
222
223
224
225
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 217

def reset_host_definitions!
  thaw_for_test!
  @platforms = {}
  @contracts.delete_if { |_id, definition| definition.owner == :host }
  @entity_requirements.delete_if { |_name, definition| definition.owner == :host }
  @bindings = {}
  @mode = :observe
  self
end

#validate!(entities) ⇒ Object

Boot-time fail-closed validation (authority §9). No-op unless the host has opted in (see opted_in?).



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/command_tower/configuration/registry/client_compatibility/config.rb', line 188

def validate!(entities)
  return self unless opted_in?

  if @platforms.empty?
    raise CommandTower::ClientCompatibility::NoConfiguredPlatformsError,
      "client_compatibility is configured but declares zero platforms"
  end

  validate_entity_requirements!(entities)
  validate_bindings!
  self
end