Repository navigation
v0.121.0
Changed (BREAKING)
-
with_read_lockandwith_write_lockdecorators removed (DEAD-RWLOCK-DECORATORS-001):- Both decorators were unreachable dead code: absent from
__all__ = ["RWLock"], not imported by any module in the codebase, not covered by any test - Deleted from
runtime/rwlock.py;Callableandwrapsimports removed alongside; the file now exports onlyRWLock - Callers must use the context-manager API directly:
with lock.read():/with lock.write():, which provide identical semantics without decorator indirection - Location:
runtime/rwlock.py
- Both decorators were unreachable dead code: absent from
-
ErrorCategorybase changed fromEnumtoStrEnum(CLARITY-ERRORCATEGORY-STRENUM-001):ErrorCategory(Enum)producedstr(ErrorCategory.REFERENCE) == "ErrorCategory.REFERENCE";.valueaccess was required for serialization and log aggregation, and thestr()output was not the plain value string- Changed to
ErrorCategory(StrEnum):str(ErrorCategory.REFERENCE) == "reference"; direct equalityErrorCategory.REFERENCE == "reference"is nowTrue(StrEnum inherits fromstr);.valueand.nameattributes are unchanged - Code comparing
str(category)against"ErrorCategory.X"strings must update to compare against the plain value strings ("reference","resolution","cyclic","parse","formatting") - Location:
diagnostics/codes.py
Fixed
-
TextElement expansion budget exhaustion abandoned entire
partsaccumulator (DEFECT-RESOLVER-TEXTELEMENT-BUDGET-001):_resolve_patternhandledPlaceablebudget exhaustion by catchingFrozenFluentError, appending the error, and breaking — preserving previously accumulatedparts;TextElementbudget exhaustion propagated the exception upward viatrack_expansion, abandoning the entirepartsaccumulator and silently discarding all previously resolved elements- Fixed:
TextElementbranch now checkscontext.total_chars > context.max_expansion_sizeafter callingtrack_expansion, appendsEXPANSION_BUDGET_EXCEEDEDerror, and breaks — symmetric behavior with thePlaceablepath; partial output is preserved regardless of which element type triggers the limit - Location:
runtime/resolver.py
-
ResolutionContext._seenwas injectable via dataclass__init__(SECURITY-RESOLUTION-CONTEXT-SEEN-001):_seen: set[str] = field(default_factory=set)was a public__init__parameter; any caller could constructResolutionContext(_seen={"target-message"})to pre-populate the cycle-detection set, makingcontext.contains("target-message")returnTrueimmediately and silently bypassing cycle detection for an arbitrary message ID without any error- Fixed: changed to
field(init=False, default_factory=set); excluded from__init__; pre-population is structurally impossible - Location:
runtime/resolution_context.py
-
Data race on
_modified_in_contextinFluentBundleandFluentLocalization(RACE-MODIFIED-IN-CONTEXT-001):_modified_in_contextwas reset toFalsein__enter__and read + reset in__exit__without holding_rwlock; concurrentadd_functionandadd_resourcecalls write_modified_in_context = Trueunder a write lock; in Python 3.13 free-threaded mode__exit__could observe a staleFalseand skip cache invalidation despite concurrent mutations having occurred- Fixed:
__enter__resets_modified_in_contextunder_rwlock.write()(bundle) or_lock.write()(orchestrator);__exit__reads and resets the flag atomically under the same write lock, then performs cache invalidation outside the lock (cache operations are independently thread-safe) - Location:
runtime/bundle.py,localization/orchestrator.py
-
_clear_tracebacksdestroyed forensic audit trail on every resolver return (FORENSIC-CLEAR-TRACEBACKS-001):_clear_tracebacksseterr.__traceback__ = Noneon everyFrozenFluentErrorbeforeresolve_messagereturned; stack traces were unconditionally destroyed at the resolver boundary, making post-mortem incident analysis impossible unless the caller logged the exception before the error tuple was discarded; this contradicted the forensic intent ofFrozenFluentError's BLAKE2b content hashing design- Fixed:
_clear_tracebacksdeleted; all sixreturn (result, _clear_tracebacks(errors))call sites changed toreturn (result, tuple(errors)); tracebacks are preserved for the full lifetime of the error objects - Location:
runtime/resolver.py
Changed
-
ResolutionContext.track_expansionno longer raises on budget exceeded (ARCH-TRACK-EXPANSION-SOC-001):track_expansionboth mutated_total_charsand generatedFrozenFluentErrorviaErrorTemplate, coupling state tracking to error policy in an infrastructure object; the resolver already had a pre-loop budget check that generated the same diagnostic via different code, making budget exhaustion handling inconsistent acrossTextElementandPlaceablepathstrack_expansion(char_count)now only mutates_total_charsand never raises; callers checkcontext.total_chars > context.max_expansion_sizeafter each call and generateFrozenFluentErrordirectly; error construction is unified in_resolve_patternfor both element paths- Location:
runtime/resolution_context.py,runtime/resolver.py
-
IntegrityCache._lockchanged fromRLocktoLock(PERF-CACHE-LOCK-001):IntegrityCache._lockwasthreading.RLock(); no call path inIntegrityCacheacquires the lock re-entrantly — every public method acquires at entry and_auditis documented to run while the lock is already held without re-acquiring;RLocktracked the owning thread ID and acquire count on every acquisition, adding measurable overhead in the hot path offormat_pattern()- Changed to
threading.Lock(); lock semantics unchanged for all current call paths; any future inadvertent re-entry would raiseRuntimeErrorimmediately rather than silently succeeding - Location:
runtime/cache.py
-
GlobalDepthGuardthread-spawning limitation documented (CLARITY-DEPTH-GUARD-THREAD-LIMIT-001):- The class docstring documented protection against same-thread re-entry via
ContextVarbut did not document the known limitation: custom functions that spawn new threads bypass the guard entirely — each new thread receivesContextVardefault (depth 0) and can initiate a full-depth resolution chain independent of the spawning thread's guard state - Added explicit "Thread Spawning Limitation" section to the docstring; no code changes; the limitation is an accepted consequence of
ContextVarsemantics - Location:
runtime/resolution_context.py
- The class docstring documented protection against same-thread re-entry via
-
resolve_messageexcept clause comment corrected (CLARITY-RESOLVER-EXCEPT-COMMENT-001):- The
except FrozenFluentErrorblock carried the comment "Global depth exceeded — collect error and return fallback"; the block catches allFrozenFluentErrorinstances regardless ofDiagnosticCode— includingEXPANSION_BUDGET_EXCEEDEDandEXPRESSION_DEPTH_EXCEEDEDfrom the expression depth guard — not only global depth violations; the misleading comment could cause developers to incorrectly narrow the guard's scope during maintenance - Replaced with: "Resolution limit exceeded (global depth, expression depth, or expansion budget). Collect error and return fallback — prevents partial output from reaching the caller."
- Location:
runtime/resolver.py
- The