Surfaced by the #1097 lane (8af4214 names the refusing authority). Semantic profile-state records (RetrievalProfileStateV1) are per scope (worktree), but the configuration revision they pin is per project. After the primary worktree's activation commits configuration revision R2, the linked worktree's profile state still pins R1; activate() authorizes and CAS-checks the grant against the state's pinned revision (RetrievalProfileStateV1::validate_cas, capability.validate), so the linked scope's set_semantic_profile is refused as a non-retryable configuration.invalid_request:
semantic configuration transition rejected: mutation authority recheck against configuration revision configuration.revision.v1.<R1> (grant revision <R2>)
The same refusal follows any non-semantic configuration commit once a scope's state exists (daemon_source_binding_rebind, additive_default_convergence, any settings change), so this is not linked-worktree-specific — it is any project with more than one scope, or one scope plus an unrelated configuration write between activations.
Reproduces deterministically as the last step of daemon::production_harness::generation_retention_test::linked_worktree_scope_retention_crash_replay_and_pure_inventory_journey with TRACEDECAY_DISTRIBUTION_FASTEMBED_FIXTURE set (hauler cc-10296 on cb211ad: the journey now passes mount, opt-in, install and both semantic generations and fails at the second set_semantic_profile).
Design decision needed (owner):
- Rebase every scope's profile state onto the project configuration revision at each configuration commit (one transaction: config write + per-scope state revision bump), keeping the CAS as-is; or
- Key the profile-state CAS on the semantic active/rollback digests + epoch that the transition actually depends on, and validate the grant against the current configuration revision rather than the one the state last saw.
Option 2 keeps the CAS honest about what it protects (the semantic state, not unrelated settings) and avoids a fan-out write per commit; option 1 is smaller but makes every configuration write O(scopes). Whichever is chosen, the refusal for a genuinely concurrent semantic transition on the same scope must remain typed and retryable-or-not as today, and the journey above must pass end to end.
Surfaced by the #1097 lane (8af4214 names the refusing authority). Semantic profile-state records (
RetrievalProfileStateV1) are per scope (worktree), but the configuration revision they pin is per project. After the primary worktree's activation commits configuration revision R2, the linked worktree's profile state still pins R1;activate()authorizes and CAS-checks the grant against the state's pinned revision (RetrievalProfileStateV1::validate_cas,capability.validate), so the linked scope'sset_semantic_profileis refused as a non-retryableconfiguration.invalid_request:The same refusal follows any non-semantic configuration commit once a scope's state exists (
daemon_source_binding_rebind,additive_default_convergence, any settings change), so this is not linked-worktree-specific — it is any project with more than one scope, or one scope plus an unrelated configuration write between activations.Reproduces deterministically as the last step of
daemon::production_harness::generation_retention_test::linked_worktree_scope_retention_crash_replay_and_pure_inventory_journeywithTRACEDECAY_DISTRIBUTION_FASTEMBED_FIXTUREset (hauler cc-10296 on cb211ad: the journey now passes mount, opt-in, install and both semantic generations and fails at the secondset_semantic_profile).Design decision needed (owner):
Option 2 keeps the CAS honest about what it protects (the semantic state, not unrelated settings) and avoids a fan-out write per commit; option 1 is smaller but makes every configuration write O(scopes). Whichever is chosen, the refusal for a genuinely concurrent semantic transition on the same scope must remain typed and retryable-or-not as today, and the journey above must pass end to end.