Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Enforcement Map

Maps every invariant to the crate, trait method, runtime check, timing, and violation response that enforces it.

Covers: INV-S1–S10, INV-S8a, INV-C1–C7, INV-K1–K5, INV-D1–D5, INV-R1–R6, INV-E1–E3, INV-N1–N5, INV-A1–A2, INV-O1–O3, INV-T1–T3, INV-W1–W4a, INV-G1–G5, INV-X1–X6. (59 invariants total.)

Legend

  • Crate: which taba crate owns the enforcement logic.
  • Trait::method: the trait and method where the check executes.
  • Check: what the code does at runtime.
  • When: the lifecycle event that triggers the check.
  • On Violation: the error variant raised and recovery action taken.

Security Invariants (INV-S1 through INV-S10)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-S1: Zero-default capabilitiestaba-securityCapabilityGuard::check_access(unit, capability)Verify the unit’s declared capabilities include the requested capability AND an approved policy exists granting it. Empty capability set = no access.On every capability access request (runtime, before workload I/O)SecurityError::CapabilityDenied. Access blocked. No fallback.
INV-S2: Security conflicts fail closedtaba-solverSolver::detect_conflicts(composition)Scan all capability declarations in a composition for incompatible pairs. If any pair is incompatible and no policy resolves it, refuse composition.On composition (solver evaluation)SolverError::ConflictDetected / SecurityError::SecurityConflictUnresolved. Composition refused. Requires policy unit creation.
INV-S3: Signed units, verified before mergetaba-security, taba-graphSignatureVerifier::verify(unit) called by Graph::merge(unit)(a) Verify Ed25519 signature over (unit || trust_domain_id || cluster_id || validity_window). (b) Confirm author had valid scope at creation time. (c) Confirm author’s key was not revoked before unit’s creation timestamp. Synchronous – blocks merge until complete.On merge (before unit enters graph state)SecurityError::SignatureInvalid, SecurityError::AuthorKeyRevoked, SecurityError::ValidityWindowExpired. Unit rejected. Not added to graph or WAL.
INV-S4: Taint propagationtaba-core, taba-securityTaintResolver::compute_taint(data_unit)Traverse provenance graph from the queried data unit back through all inputs. Compute union (most restrictive) of all input classifications. Compare against the unit’s declared classification.On query (not merge). Taint is eventually consistent across nodes.SecurityError::TaintWidening if classification weaker than computed taint without policy. Query returns the computed (restrictive) classification regardless.
INV-S5: Author scope enforcementtaba-securityScopeValidator::validate_author_scope(author, unit)Check that author’s (unit_type_scope, trust_domain_scope) includes the unit’s type and trust domain.On unit creation and on merge (pre-merge gate, part of INV-S3 verification)SecurityError::AuthorScopeExceeded. Unit rejected.
INV-S6: Multi-party trust domain creationtaba-securityMultiPartyValidator::validate_signers(governance_unit, required_count)Count distinct author signatures on the trust domain creation governance unit. Require >= 2.On trust domain creation (before merge of governance unit)SecurityError::TrustDomainCreationRequiresMultiParty. Creation refused.
INV-S7: Data hierarchy narrowing/wideningtaba-core, taba-securityClassificationValidator::validate_hierarchy(child, parent)Compare child classification against parent using lattice (public < internal < confidential < PII). Narrowing (child more restrictive) always allowed. Widening (child less restrictive) requires an explicit declassification policy.On data unit insertion (child added to parent)SecurityError::TaintWidening. Child rejected unless policy exists.
INV-S8: Unique author scopestaba-securityScopeValidator::check_uniqueness(author, scope, existing_assignments)Before persisting a role assignment governance unit, scan all existing role assignments. Reject if any other author already holds the identical (unit_type_scope, trust_domain_scope) tuple.On role assignment creation (before merge of governance unit)SecurityError::ScopeDuplicate. Role assignment rejected.
INV-S9: Multi-party declassificationtaba-securityMultiPartyValidator::validate_declassification(policy)Count distinct signers on the declassification policy. Require >= 2: one with policy scope, one with data-steward scope.On declassification policy creation (before merge)SecurityError::DeclassificationRequiresMultiParty. Policy rejected.
INV-S10: Multi-party trust domain (threshold)taba-securityMultiPartyValidator::validate_signers(governance_unit, required_count)Same mechanism as INV-S6. Verify the policy unit lists required signers and all required cryptographic signatures are present.On trust domain creation (before merge)SecurityError::TrustDomainCreationRequiresMultiParty. Creation refused.

Consistency Invariants (INV-C1 through INV-C7)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-C1: Graph is single source of desired statetaba-graph, taba-coreGraph::query(predicate) (architectural – no other state store exists)Structural enforcement: no alternative desired-state store is implemented. All desired-state reads go through Graph::query. Solver reads only from graph.Always (architectural constraint)N/A – violation is a design bug, not a runtime error. Code review and architecture enforcement.
INV-C2: CRDT merge propertiestaba-graphCrdtMerge::merge(local_state, remote_state)Property-based tests verify commutativity (merge(A,B) == merge(B,A)), associativity, and idempotency. Runtime: merge is implemented as set union with LWW timestamps – algebraically guarantees properties.On every merge (gossip reconciliation, peer sync)GraphError::CrdtViolation. Internal bug. Merge aborted. Alert raised. Node should not apply partial merge.
INV-C3: Solver determinism (fixed-point, no float)taba-solverSolver::compute_placement(graph_state, membership)All arithmetic uses Ppm/SignedPpm (u64/i64 fixed-point). Tiebreaker: lexicographically lowest NodeId. Compile-time lint: #[deny(clippy::float_arithmetic)] on solver crate. Runtime: hash solver output and compare across nodes via gossip.On placement computation. Cross-node verification via gossip protocol.SolverError::DeterminismViolation, SolverError::FloatingPointDetected. Placement paused. Critical alert.
INV-C4: WAL-before-effecttaba-graphWal::append(entry) called before Graph::apply(mutation)WAL entry (Merged/Pending/Promoted) is fsync’d to disk before the in-memory graph state is updated. If WAL write fails, mutation is not applied.On every mutation (insert, promote, merge)GraphError::WalWriteFailed. Mutation rejected. Node stops accepting new mutations until WAL is writable.
INV-C5: Policy references existing conflictstaba-corePolicyValidator::check_references(policy)On query: verify that the conflict tuple referenced by the policy still exists in the graph. Orphaned policies (referencing non-existent conflicts) are flagged for archival.On query (not merge). Validity checked lazily.CoreError::OrphanedPolicy. Policy ignored in solver decisions. Eligible for archival.
INV-C6: Insertion-order independencetaba-solver, taba-graphSolver::compute_composition(units)Solver re-evaluates all affected compositions on any unit addition. Capability lists sorted lexicographically before matching (INV-K2). Property tests verify same output regardless of insertion order.On composition (after any unit insert/merge)GraphError::InsertionOrderDependence. Internal bug. Critical alert.
INV-C7: Single non-revoked policy per conflicttaba-corePolicyValidator::check_uniqueness(policy, conflict_tuple)Before accepting a new policy for a conflict tuple, check if a non-revoked policy already exists. If yes, the new policy must have a supersedes field referencing the existing one. Solver uses latest non-revoked version in the chain.On policy creation/mergeCoreError::PolicyConflict. New policy rejected unless it explicitly supersedes existing.

Composition Invariants (INV-K1 through INV-K5)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-K1: All capability needs satisfiedtaba-solverSolver::match_capabilities(composition)For every needs declaration in the composition, find a matching provides with compatible type. Unmatched needs cause composition failure.On composition (solver evaluation)SolverError::CapabilityUnsatisfied, CoreError::CompositionFailed. Composition refused.
INV-K2: Typed capability matchingtaba-solverCapabilityMatcher::matches(need, provide)Compare (type, name, purpose?) tuples. Type must be compatible (exact or subtype). Name must match. If purpose is declared on either side, it must match (purpose mismatch triggers conflict requiring policy). Capabilities sorted lexicographically before matching.On composition (during capability matching)CoreError::CapabilityTypeMismatch. If purpose mismatch: SolverError::ConflictDetected.
INV-K3: Placement respects tolerancestaba-solverSolver::score_placement(unit, node)Evaluate unit tolerance declarations (latency budget, failure mode, resource requirements) against each candidate node’s reported capabilities. Node must satisfy all constraints.On placement (after composition)CoreError::PlacementConstraintViolation, SolverError::NoViableNode. Unit not placed. Retryable if nodes join.
INV-K4: Scaling from unit-declared parameterstaba-solverSolver::compute_scaling(unit)Scaling (min/max instances, triggers) read exclusively from unit declaration. Solver does not invent or infer scaling logic. Validate that min <= max, triggers are well-formed.On placement and periodic re-evaluationCoreError::ScalingParameterInvalid. Unit flagged. No scaling applied until fixed.
INV-K5: Cyclic recovery dependencies fail closedtaba-solverSolver::detect_cycles(recovery_graph)Build directed graph of recovery dependencies. Run cycle detection (Tarjan’s or DFS). If cycle found, require explicit policy declaring restart priority. Tiebreaker without policy: lexicographically lowest UnitId gets priority.On composition (after recovery dependency resolution)CoreError::CyclicRecoveryDependency, SolverError::CyclicDependency. Composition refused until policy breaks cycle.

Data Invariants (INV-D1 through INV-D3)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-D1: Unbroken provenance chaintaba-coreProvenanceValidator::validate_chain(data_unit)Every data unit produced by a workload must reference its input data units and producing workload. On query, traverse provenance links and verify all referenced units exist. On merge, units with unsatisfied references enter pending queue (causal buffering per INV-C4).On query (full validation). On merge (reference check, causal buffering).CoreError::ProvenanceChainBroken. On merge: unit enters pending state. On query: provenance gap flagged.
INV-D2: Retention enforcementtaba-coreRetentionEnforcer::check_expiry(data_unit)Compare data unit’s retention duration against current time. Expired units are marked eligible for compaction. Compaction is not optional.Periodic (compaction sweep) and on queryCoreError::RetentionExpired. Unit eligible for compaction. Compactor removes from active graph.
INV-D3: No redundant childrentaba-coreHierarchyValidator::check_redundancy(child, parent)Compare child constraints against parent constraints. If identical, child is redundant and should not exist. Also enforce max hierarchy depth (16 levels).On data unit insertion (child creation)CoreError::RedundantChild, CoreError::HierarchyDepthExceeded. Child rejected.

Resilience Invariants (INV-R1 through INV-R6)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-R1: Node failure does not corrupt graphtaba-erasureErasureCodec::reconstruct(shards), ReconstructionScheduler::schedule(priority_queue)Reconstruct graph shards from surviving erasure-coded shards. After reconstruction, re-verify all unit signatures (via SignatureVerifier::verify). Backpressure: throttled rate, prioritized by shard criticality (governance > policy > data constraints > workload). Circuit breaker when queue depth exceeds threshold.On node failure detection (from gossip)ErasureError::ReconstructionFailed, ErasureError::ReconstructionBackpressure, ErasureError::SignatureVerificationPostReconstruct. If below threshold: degraded mode.
INV-R2: Partition consistency via CRDTtaba-graph, taba-gossipCrdtMerge::merge(local, remote), PartitionResolver::resolve_duplicates(placements)Both partition sides maintain graph via CRDT. On heal: merge produces correct state (INV-C2). Duplicate placements resolved by deterministic tiebreaker (lowest NodeId wins, INV-C3). Loser drains. Role-carrying units disabled on minority side.On partition heal (gossip reconnection and merge)Duplicate placement: loser receives drain signal. Role units on minority side: NodeError::DegradedMode for affected operations.
INV-R3: Gossip convergence with signed messagestaba-gossipGossipProtocol::verify_message(msg), FailureDetector::declare_failure(node_id, witnesses)All gossip messages verified with sending node’s Ed25519 key. Membership state changes (node declared failed) require corroboration from >= 2 independent witnesses (configurable via ClusterConfig::witness_count).On every gossip message receipt. On failure declaration.GossipError::MessageSignatureInvalid (message dropped). GossipError::InsufficientWitnesses (failure declaration deferred until witnesses met).
INV-R4: Shard reconstructability thresholdtaba-erasureErasureCodec::check_threshold(available_shards, k, n)Compute k = ceil(N * (1 - R/100)). If actual failures > floor(N - k), enter degraded mode and surface operator alert.On node failure detection and periodically during health checksErasureError::ThresholdExceeded, ErasureError::InsufficientShards. System enters degraded mode. Operator alert.
INV-R5: Suspected nodes stay in placement pooltaba-gossip, taba-solverSolver::score_placement(unit, node) with NodeHealth::Unknown handlingSuspected nodes remain in pool with health=‘unknown’. Solver deprioritizes them (lower score) but does not remove. Only SWIM multi-probe consensus confirms failure and triggers removal.On placement (solver considers suspected nodes as last resort). On gossip protocol probe cycle.No error – behavioral: solver avoids suspected nodes when alternatives exist. GossipError::NodeUnreachable tracks probe failures.
INV-R6: Graph memory limit with auto-compactiontaba-graph, taba-nodeMemoryMonitor::check_usage(graph), Compactor::compact(graph, target_bytes)Monitor active graph memory. At 80% of ClusterConfig::graph_memory_limit_bytes, trigger auto-compaction. At 100%, node enters degraded mode: refuses new placements. Governance units are fully replicated (not just erasure-coded).Periodic (memory monitor interval). On every graph mutation (lightweight size check).GraphError::MemoryLimitExceeded, NodeError::DegradedMode. At 80%: compaction runs. At 100%: new placements refused until compaction completes.

Environment & Promotion Invariants (INV-E1 through INV-E3)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-E1: Promotion policy gates placement by envtaba-solverPromotionEvaluator::evaluate(unit, promotions, gates), CapabilityFilter::filter(unit, nodes, promotions)Workload can only be placed on nodes whose env tag matches a promotion policy. Exception: env:dev uses author affinity (no promotion needed).On placement (solver evaluation)SolverError::NoCapableNode with detail “no promotion policy for env:X”. Unit not placed.
INV-E2: Promotions are cumulativetaba-solverPromotionEvaluator::evaluate()Promotion to env:prod does NOT remove from env:test. Environments are independent targets. Solver evaluates each environment independently.On placement (solver evaluation)Structural — solver evaluates each env separately. No violation error.
INV-E3: No PromotionGate = all autotaba-solverPromotionEvaluator::evaluate()If no PromotionGate governance unit exists, all transitions default to auto-promote. Zero config = full auto.On promotion evaluationStructural — default behavior when governance unit absent.

Node Capability Invariants (INV-N1 through INV-N5)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-N1: Auto-discovery on startup, cachedtaba-nodeCapabilityDiscoverer::discover(), CapabilityDiscoverer::refresh()Node probes system for capabilities at startup. Cache is authoritative until refresh. Fleet refresh via governance OperationalCommand.On node startup. On taba refresh. On fleet RefreshCapabilities command.NodeError::CapabilityDiscoveryFailed. Node starts with empty capability set (no workloads placed).
INV-N2: Capabilities are hard constraintstaba-solverCapabilityFilter::filter(unit, nodes, promotions)Binary match: artifact.type must match node runtime capability. No fallback.On placement (solver capability filter)SolverError::NoCapableNode. Unit not placed.
INV-N3: Resources are soft constraintstaba-solverResourceRanker::rank(unit, nodes, resources)Among capability-matched nodes, rank by resource availability. Ppm arithmetic, versioned snapshots.On placement (after capability filter)No error — ranking always produces an ordering. Worst-fit node still valid if capabilities match.
INV-N4: Custom tags match like capabilitiestaba-solverCapabilityFilter::filter()Freeform key:value tags checked identically to auto-discovered capabilities in solver matching.On placement (capability filter)Same as INV-N2 — SolverError::NoCapableNode if required tag not found.
INV-N5: Placement-on-failure defaults by envtaba-solver, taba-nodeSolver::handle_node_failure(unit, env), Reconciler::reconcile()env:dev defaults to leave-dead. Other envs default to auto-replace. Per-unit placement_on_failure overrides.On node failure detection (gossip → solver re-evaluation)Behavioral — not an error. Dev workloads left, prod workloads re-placed.

Artifact Distribution Invariants (INV-A1 through INV-A2)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-A1: SHA256 digest verificationtaba-nodeArtifactFetcher::fetch(ref, digest)After fetching artifact (from peer or external), compute SHA256 and compare to expected digest. Mismatch = reject, report to graph.On artifact fetch (before workload execution)NodeError::ArtifactDigestMismatch. Artifact rejected. Retry from different source. Report to graph.
INV-A2: Peer cache firsttaba-nodeArtifactFetcher::fetch()Check peer cache before external source. Optimization, not security boundary — INV-A1 digest verification is the integrity guarantee.On artifact fetchBehavioral — if peer has artifact, skip external download.

Observability Invariants (INV-O1 through INV-O3)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-O1: Every solver run produces decision trailtaba-observeDecisionTrailRecorder::record(snapshot, membership, result, version)Node calls recorder after each solver invocation. Trail persisted to graph via WAL.After every solver runObserveError::TrailNotFound if recording fails. Alert. Solver continues (observability failure does not block placement).
INV-O2: Trail retention since last compactiontaba-observe, taba-graphDecisionTrailQuery::query_by_range(), Compactor::compact()Default retention = since last compaction. Unit decision_retention or governance policy can extend. Compaction respects retention.On compaction sweep and trail queryObserveError::TrailCompacted if querying beyond retention.
INV-O3: Progressive health checkstaba-nodeHealthCheckOrchestrator::run_checks()Default: OS-level process monitoring. If HealthCheck declared: execute that instead. Node never skips monitoring.Periodic (configurable interval per workload)Health failure reported to graph as HealthStatus. Triggers failure semantics (restart, re-place).

Logical Clock Invariants (INV-T1 through INV-T3)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-T1: Monotonic logical clock, sync on communicationtaba-commonLogicalClock::tick(), LogicalClock::sync(remote)Every system action increments. On inter-node communication: local = max(local, remote) + 1.On every action and every gossip/merge messageStructural — u64 overflow at 1.8×10^19 events. No practical violation.
INV-T2: Dual clock modeltaba-commonDualClockEvent triple recorded on every eventLogical clock for ordering, wall clock for retention/compliance. Both recorded. System chooses by operation type.On every eventStructural — type system enforces recording both clocks.
INV-T3: Causal revocationtaba-securityKeyManager::is_revoked_in_local_graph(author)Check if revocation governance unit exists in local graph. No clock comparison. Optional grace window fallback.On unit merge (pre-merge gate)SecurityError::KeyRevoked. Unit rejected.

Workload Lifecycle Invariants (INV-W1 through INV-W4a)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-W1: Services valid indefinitelytaba-coreUnitHeader::validity = NoneNo validity window → no automatic expiry. Terminated only explicitly or by drained placement. Key revocation does NOT invalidate existing services.Structural (type: validity is Option)N/A — services never auto-expire.
INV-W2: Bounded tasks auto-terminatetaba-nodeReconciler::reconcile(), deadline check in reconciliation loopCheck three triggers: completion (exit 0), failure (retries exhausted), deadline (LC range or wall time). Any trigger → terminate.On task exit, on periodic reconciliation (deadline check)Task transitions to Terminated. Ephemeral data eligible for removal (INV-D4).
INV-W3: Spawn depth enforced at mergetaba-graph, taba-coreGraph::merge() pre-check, SpawnContext::spawn_depth validationOn merge: traverse spawn provenance chain, compute depth. Reject if > max (default 4).On graph merge of spawned task unitGraphError::SpawnDepthExceeded. Unit rejected.
INV-W4: Delegation token signingtaba-securityDelegationValidator::validate(token, spawned_unit_lc)Verify: (a) token signature valid, (b) LC range covers spawned task, (c) spawn count ≤ max, (d) token not revoked, (e) parent active.On graph merge of delegation-signed unitSecurityError::DelegationExpired, DelegationSpawnLimitExceeded, DelegationTokenForged. Unit rejected.
INV-W4a: No governance via delegationtaba-securityDelegationValidator::check_governance_block(token, unit_type)Spawned tasks cannot create policy, governance, or declassification units. Hard block at merge.On graph merge of delegation-signed unit with policy/governance typeSecurityError::DelegationGovernanceBlocked. Unit rejected.

Data Lifecycle Invariants (INV-D4 through INV-D5)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-D4: Ephemeral data reference checktaba-graphCompactor::remove_ephemeral(data_unit)On producing task termination: check downstream references. Has refs → tombstone. No refs → full remove. Governance can mandate tombstone for all.On bounded task terminationBehavioral — no error. Reference check determines treatment.
INV-D5: Local-only requires policy for classified datataba-core, taba-securityRetentionValidator::validate_local_only(data_unit)If retention = LocalOnly and classification > Public, require explicit policy authorization.On unit creation / validationCoreError::LocalOnlyRequiresPolicy. Unit rejected without policy.

Compaction Invariants (INV-G1 through INV-G5)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-G1: Compaction eligibility deterministictaba-graphCompactor::compute_eligible(graph)Given same graph state, all nodes agree on eligible units. Eligibility derived from graph state only (terminated status, retention expiry, supersession).On compaction sweepStructural — same algorithm on same input = same output.
INV-G2: Tombstones preserve provenancetaba-graphCompactor::tombstone(unit)Tombstone retains UnitId, AuthorId, type, LC range, termination reason, references, original digest.On compaction (tombstone creation)Structural — Tombstone struct includes required fields.
INV-G3: Governance never compactedtaba-graphCompactor::is_exempt(unit)Skip governance units, active policies, root ceremony chain during compaction sweep.On compaction sweepStructural — exemption check before tombstoning.
INV-G4: Eviction ≠ compactiontaba-nodeMemoryManager::evict(unit)Eviction drops content locally without tombstone. Unit remains live. Content recoverable from peers.On local memory pressure (node-specific)Behavioral — eviction is transparent to graph state.
INV-G5: Compaction priority ordertaba-graphCompactor::compact(graph, target_bytes)Priority: ephemeral → trails → terminated tasks → superseded policies → terminated services → expired data. Mirrors reconstruction priority inverse.On compaction sweepStructural — priority ordering in compaction algorithm.

Cross-Trust-Domain Invariants (INV-X1 through INV-X6)

InvariantCrateTrait::methodCheckWhenOn Violation
INV-X1: Bilateral policy for cross-domaintaba-gossip, taba-solverCrossDomainGossip::validate_bilateral(query), CapabilityFilter::filter()Before executing forwarding query, verify bilateral policy in both domains. Missing either side = fail closed.On cross-domain forwarding queryGossipError::BilateralPolicyMissing. Query rejected. Composition blocked.
INV-X2: Read-only cross-domain viewstaba-gossipCrossDomainGossip::forward_query() returns ForwardingResultQuery results NOT merged into querying domain’s graph. Reference by UnitId only.On forwarding query responseStructural — ForwardingResult is a read-only type, not a mergeable unit.
INV-X3: Fail-open cache defaulttaba-gossipCrossDomainGossip::query_with_cache(cache_policy)Default: serve stale if bridge down. Governance override: fail closed for freshness.On cross-domain query with bridge unavailableBehavioral — cache served (default) or GossipError::BridgeUnavailable (strict mode).
INV-X4: Emergent bridge defaulttaba-gossipCrossDomainGossip::discover_bridges(target_domain)Any node in multiple domains is a bridge. Governance can restrict to designated only.On bridge discoveryBehavioral — gossip queries all multi-domain nodes (or designated only).
INV-X5: Cross-domain capability via governancetaba-gossipCrossDomainGossip::propagate_advertisement(capability_def)Bridge nodes relay CrossDomainCapability governance units across boundaries.On governance unit merge (bridge detects cross-domain advertisement)Behavioral — bridge gossips the advertisement to other domains.
INV-X6: No bridge = unresolved capabilitytaba-solver, taba-gossipCapabilityFilter::filter(), CrossDomainGossip::discover_bridges()If no bridge to target domain, solver surfaces as unresolved. No auto-creation.On cross-domain composition attemptSolverError::NoCapableNode with detail “no bridge to domain X”. Alert raised.

INV-S8a: Overlapping Scopes for Decision-Making Types

InvariantCrateTrait::methodCheckWhenOn Violation
INV-S8a: Overlapping policy/governance scopestaba-securityScopeValidator::check_uniqueness(author, scope, existing, unit_type)For policy/governance types: skip uniqueness check (overlapping permitted). For workload/data: enforce INV-S8 (strict uniqueness).On role assignment creationStructural — check branches on unit_type.