ocr_stabilizer 0.5.0
ocr_stabilizer: ^0.5.0 copied to clipboard
Real-time OCR overlay stabilization engine — drift correction, spatial indexing, block tracking. Built for Flutter.
0.5.0 - 2026-05-24 #
Quality-polish release. Additive public-API additions plus a latent
engine bug fix in the band-fallback counter accounting. No breaking
changes from 0.4.x — ^0.5.0 is a safe upgrade.
Added #
BandPredicateException— typed wrapper class for throws raised by a consumer-suppliedBandSpatialPredicate. The engine catches the predicate's error inside_findMatchand rewraps it asBandPredicateException(cause, predicateStackTrace). Predicate failures now surface with a typed shape; no silent swallow. Exposescause,predicateStackTrace,message, and an asserting ctor that rejects double-wrapping (#35).BandFallbackStats.rejectedTextBandcounter ticks at the text-band-miss site inside_findMatch. Makes the band funnel decomposable:rejectedCandidateFloor + rejectedSpatial + rejectedTextBand + bandMatchesIdentified == candidatesConsidered, invariant to admit-mode early-exit (#34).- Library-level dartdoc in
lib/ocr_stabilizer.dartnow enumerates the headline types (StabilizationEngine,BandFallbackConfig,BandFallbackStats,BandPredicateException, block hierarchy, confidence types) plus the recommended adoption flow (off → observeOnly → admit) (#35). - Internal
assertConfidenceRange(field, raw, {prefix})utility atlib/src/internal/confidence_validation.dartcentralises the[0.0, 1.0]finite-double predicate. Adopted at five sites:DefaultTrackedBlockctor,MergeResultctor,StabilizationEngine._assertValidConfidence(called bystabilizeandmerge),PositionConfidence.from,TextConfidence.from. Future tightening happens in one place (#31).
Changed #
BandFallbackStats.matchesAdmittedis now incremented at the resolution-time site (where_findMatchactually returns a band match), not at scan-time. Before this release, the counter ticked as soon as a candidate was locked asbandAdmitted— so when a later primary candidate in the same scan superseded it, the counter overcounted and disagreed with the function's return value. New semantics:matchesAdmittedis exactly "band matches returned by_findMatch", and the documented invariantmatchesAdmitted <= bandMatchesIdentifiedis strengthened by the precedence rule "primary always wins, even if a band candidate was locked first" (#34).MergeResultctor's confidence-range error message wording upgraded from'must be in [0.0, 1.0]'to'must be a finite double in [0.0, 1.0]'to match the message at the engine entry guard andDefaultTrackedBlockctor — a consumer catchingArgumentErrorno longer sees two slightly different stories about the same invariant (#31).
Fixed #
- Library dartdoc no longer references a non-existent
stabilize(fresh, captureRect)signature — the actual signature isstabilize(List<T> freshBlocks). Caught by the comment-analyzer pass on PR #42 (#35). - Privacy scrub: removed 17 references to the downstream consumer's internal project name / file:line paths from spec/plan docs and one source comment (#33).
Internal #
- New CI job: pana scoring on ubuntu-latest with
--exit-code-threshold 0pinned to pana0.23.12. Authoritative pre-publish score check — Windows local pana hits 150/160 due to upstreamdart-lang/dartdocissue #4180 (CRLF offsets in Flutter SDK@docImportfiles), but Linux scoring is 160/160 (#36). - Renamed
docs/→doc/for the Pub layout-convention hint (#37). - Test count: 277 (was 270 in v0.4.0).
0.4.0 - 2026-05-23 #
Added #
BandFallbackModeenum (off|observeOnly|admit) configures the band-relaxed fallback path insideStabilizationEngine._findMatch. Default isoff; switch toobserveOnlyto readBandFallbackStatsbefore committing toadmit. Seedoc/superpowers/specs/for the full design and default provenance (#20).BandFallbackConfigvalue type wraps the band thresholds, candidate observation floor, provisional-capture grant, and spatial confirmation predicate. Constructorasserts on out-of-range values (preserves const-constructibility); engine constructor throwsArgumentErrorfor release-build safety. Primary-path floors (Lev 0.70 / Jaccard 0.80) are engine-owned, not configurable through this type (#20).BandFallbackStatsexposes per-capture counters:primaryMatchesAdmitted,primaryMatchesRejected,candidatesConsidered,rejectedCandidateFloor,rejectedSpatial,bandMatchesIdentified,matchesAdmitted. Read-only public surface; engine mutates via a same-libraryInternalsubclass. Reset viareset(); the engine never resets it automatically (#20).BandSpatialPredicatetypedef mirrorsContextualInvalidationCheck—bool Function(TrackedBlock fresh, TrackedBlock candidate). WhenBandFallbackConfig.spatialConfirmisnull, the engine substitutes a drift-awareoverlapRatio >= 0.80closure (#20).StabilizationEngineconstructor gains abandFallback: BandFallbackConfigparameter (defaults tomode: off— backward compatible) and abandStatsgetter returning the read-only stats view (#20).
Changed #
- Breaking:
StabilizationEngine.stabilize()andStabilizationEngine.merge()now throwArgumentErrorif any observation'spositionConfidence.rawortextConfidence.rawisNaNor outside[0.0, 1.0]. Catches anyTrackedBlockimplementor at the engine entry, closing the documented unchecked-const-Confidence gap (#27). - Breaking:
DefaultTrackedBlockconstructor throwsArgumentErrorwhenpositionConfidence.rawortextConfidence.rawisNaNor outside[0.0, 1.0]. Early-fail at construction with a cleaner stack trace than the engine-entry guard would produce. Consumers going throughPositionConfidence.from()/TextConfidence.from()(validated since #19) are unaffected (#27). StabilizationEngine._findMatchprimary path now usesTextDedupUtils.isTextSimilarWithScores(Lev OR Jaccard) instead ofnormalizedLevenshteinalone. Floors are unchanged (Lev 0.70 / Jaccard 0.80); the metric set widens — character-reordered text with the same significant-character set now matches on the primary path where it previously fell through (#20).
Fixed #
OverlapResolver.qualityScoreno longer silently propagatesNaNinto the NMS comparison. NaN reachingqualityScoreis now a debug-timeAssertionError; release builds skip the check (defended by engine entry validation, above) (#27).StabilizationEngineconstructor now rejectsNaNand±InfinityforBandFallbackConfig.bandLevenshteinFloorandbandJaccardFloorin release builds. The bare range check (value < 0 || value >= floor) evaluatedfalseforNaNunder IEEE 754 and let it bypass the defense; theassertin the const ctor catches it in debug only. Now short-circuits on!isFinitebefore the range check (#20).
0.3.0 #
A breaking release bundling four API changes. Pre-1.0, breaking changes take a minor version bump.
Breaking #
ObservableBlockno longer declaresexclusionHitCount, andDefaultTrackedBlockno longer carries it. The field was inert engine-side — never read or written by merge, dedup, drift correction, or the spatial index. It is consumer-managed state and does not belong on the package's block contract. Migration: a consumer with@override int get exclusionHitCount;on its own block class removes the@overrideannotation — the field stays, it is just no longer an interface member. Consumers that do not need the field drop it entirely. (#23)PositionConfidence.from/TextConfidence.fromnow throwArgumentErroron out-of-range or NaN input. Previously they validated withassertonly, which is stripped in release builds — an invalid confidence passed silently in production. Migration: catchArgumentErrorinstead ofAssertionError; any code that relied on release-mode silent acceptance of an out-of-range value now gets a thrown exception. The primaryconst PositionConfidence(double)/const TextConfidence(double)constructor stays public and unchecked — it is the onlyconst-capable path, kept forconstliteral sentinels. (#26)
Changed #
StabilizationEngine.stabilize()now rebuildsspatialIndexinternally from its returnedstableBlocksbefore returning. The old caller contract (callspatialIndex.rebuild(result.stableBlocks)after everystabilize()) is retired, along with the debug-mode staleness guard. Callers can drop the post-stabilize()rebuild call — a redundant rebuild is harmless, so this is non-breaking. (#24)MergeResult's confidence boundary checks now also reject NaN (NaN fails both< 0and> 1.0, so it previously slipped through). (#26)
Added #
StabilizationEngine.resetDriftPropagation()— clears the engine's regional-drift baseline so a consumer can reset propagation state on a session boundary (page navigation, context reset) without a stale baseline triggering a spurious correction. (#25)
0.2.2 #
Added #
- CI: GitHub Actions workflow running
flutter analyze+flutter teston push and PR (#15). CONTRIBUTING.mddocumenting dev setup, conventions, and release flow (#16).
0.2.1 #
Docs + metadata polish. No API change. First pub.dev-shipped release of the v0.2.x line.
Documentation #
- README: install snippet updated to
^0.2.1with a breaking-change pointer back to the 0.2.0 typed-confidence migration. (#14) - README:
TrackedBlock<T>example now lists all 14 getters (was missinginnerScrollerTop,sourceQuality) with a follow-up note pointing integrators atDefaultTrackedBlock<T>orObservableBlock<T>as appropriate. (#14) - README: API Reference tables refreshed for v0.2.x — corrected getter
count, generic on
ObservableBlock<T>, documentedDefaultTrackedBlock<T>,PositionConfidence,TextConfidence, plus the previously-undocumented exports (StabilizationEngine,BlockClassifierService,OverlapResolver,BlockKeyGenerator,MergeResult,StabilizationResult,ClassificationResult,TextVote,IqrOutlier,TextDedupUtils). (#14)
Metadata #
pubspec.yamlgainshomepage:and pub.devtopics:(ocr,overlay,tracking,slam,flutter). (#14).gitignoreexcludes local agentic-scaffolding directory (.ultra/). (#14)
0.2.0 #
Breaking #
TrackedBlock.positionConfidenceandtextConfidencenow return typedPositionConfidence/TextConfidenceextension types (overdouble) instead of rawdouble. Consumers implementingTrackedBlockmust update the getter signatures. The migration path:
Producer sites wrap raw doubles via- final double positionConfidence; - final double textConfidence; + final PositionConfidence positionConfidence; + final TextConfidence textConfidence;.from(value)(range-asserted), or use the.groundTruthsentinel (= 1.0) for deterministic origins. Extension types are zero-cost at runtime.
Added #
PositionConfidence/TextConfidenceextension types inlib/src/types/confidence_types.dart. Range[0.0, 1.0]enforced via.from()factory;.groundTruthstatic const sentinel for DOM/deterministic origins. (#10)DefaultTrackedBlock<T>— concrete reference implementation ofObservableBlock<T>with documented defaults for every required field (notablycarouselIdVotes: {-1: 1}— the engine's phantom-vote sentinel). IncludescopyWithandapplyMerge(MergeResult)convenience. (#5)SpatialBlockIndex.isEmpty— O(1) accessor (was:allBlocks.isEmptyallocated aSet.identity()per call). (#2)StabilizationEngine.stabilize()debug-mode staleness warning when the spatial index appears empty after a non-empty previous call, plus prominent "Caller contract" docstring documenting the consumer's rebuild responsibility. (#2)MergeResultnow throwsArgumentError(not justassert) when invariants are violated, including the confidence-range bypass via the unvalidatedPositionConfidence(double)primary constructor. Asserts strip in release; this guards engine output that flows into consumer caches. (#10)
Changed #
- SDK constraint relaxed from
sdk: ^3.8.1tosdk: ^3.3.0(extension types shipped in Dart 3.3 — the only modern feature this package uses).flutterconstraint pinned to>=3.19.0(the Flutter that bundled Dart 3.3).flutter_lintsdev-dep pinned to^5.0.0to keep dev-deps SDK floor consistent with the package SDK floor. (#3) DriftTrackerrolling windows switched fromList(O(N)removeAt(0)) todart:collection'sQueue(O(1)removeFirst). Type now signals FIFO ring-buffer intent at the declaration site. (#4)DefaultTrackedBlockconstructor throwsArgumentError(not just asserts) when thecontainerId/isInnerScrollerChildinvariant is violated — the reference implementation is state-owning, asserts strip in release. (#5)
Fixed #
SpaceKey.regionIndexreturns 0 as a safe fallback instead of throwingFormatExceptionon malformed keys (forward-compat for externally- constructed or future-format-extension keys). (#1)
Internal #
- New test files:
test/space_key_test.dart,test/confidence_types_test.dart,test/merge_result_test.dart,test/default_tracked_block_test.dart. Test count: 207 (was 180 in v0.1.0).
0.1.0 #
Initial release.
Core #
StabilizationEngine— SAR merge, intra-batch dedup, contradiction detectionDriftTracker— regional drift correction with submap isolationSpatialBlockIndex— grid-cell spatial index for O(cells) overlap queriesOverlapResolver— spatial NMS with language-aware thresholdsBlockKeyGenerator— position+text dedup keys with fuzzy neighbor matchingBlockClassifierService— OCR group classification (fixed/sticky/carousel/IC/normal)
Types #
TrackedBlock<T>/ObservableBlock<P>— block identity interfacesAbsoluteRect— zero-cost coordinate-space safety (extension type)SpaceKey,ContainerId,ScrollContext,StickyFallback— value types
Utilities #
TextDedupUtils— Levenshtein, Jaccard, CJK detectionRobustStats— median, MAD, IQRIqrOutlier— Tukey fence outlier detection