ocr_stabilizer 0.9.0
ocr_stabilizer: ^0.9.0 copied to clipboard
Real-time OCR overlay stabilization engine — drift correction, spatial indexing, block tracking. Pure Dart; Flutter-ready.
0.9.0 - 2026-07-23 #
Validation release for the opt-in agreementWeighted model (#58 data
arc): production-capture replay across three stream regimes (stable /
reflow / heavy OCR jitter), two numerics fixes, and the replay tooling
that produced the evidence. Default behavior is unchanged — legacy
consumers can upgrade without review.
Changed (opt-in agreementWeighted numerics only) #
- The agreement scale is now a jitter allowance: 3× the regional
median block height (#70, #73). The 0.7.0 drift-margin-derived scale
was removed: median-of-drift is a systematic-offset measure — ~0 under
symmetric jitter and pure numeric residue on stable streams — so it
collapsed position confidence on unmoving blocks (1.0 → 0.34, fixed
by the #70 floor) and was unreachable everywhere else. Separately, the
sweep showed the un-tuned 1× fallback scale let the confidence-anchored
merge weight chase deep-chain jitter at 15.8 px/merge (worse than
legacy). Post-change: deep-chain jitter damps to 3.8 px/merge
(legacy: 11.8) while confidence stays regime-discriminating
(~1.0 stable / 0.85 reflow / 0.35 heavy jitter). Sweep evidence:
doc/replay/validation/2026-07-scale-sweep/. Slated to become the 1.0 default (#74).
Added #
- Replay rig for consumer-captured observation streams (#68):
dart tool/replay/replay.dart <freeze-report|ab-report|live-report> <capture.jsonl>grades captured streams against the engine — freeze semantics (#57), position-model A/B (#58), and consumer-side lifecycle views. JSONL schema contract:doc/replay/capture_schema.md.
Decided #
- Provisional-freeze semantics stay evidence-free (#57, #69, #76): frozen captures accrue no observation count, text votes, or position. Decision + re-armed re-open triggers are codified at the freeze path; the one nonzero-traffic counterfactual observed (noisy-OCR dwell, admit-mode replay) was tail magnitude — 1 chain, 3 freezes, 2 discarded high-confidence votes per ~5-minute session.
0.8.0 - 2026-07-22 #
The package is now pure Dart (#59): no Flutter SDK dependency, usable in server-side Dart (PDF and camera OCR pipelines) and CLI tools as well as Flutter apps. Same behavior, new geometry types — read the migration notes below.
Breaking #
- Geometry types moved off
dart:ui.Rect,Offset, andSizeare now package-owned value types exported from the barrel (lib/src/types/geometry.dart), member-compatible with theirdart:uicounterparts and matching their semantics exactly (strictoverlapson edge-touching rects, negative-sizeintersectfor disjoint rects,Rect.lerpincl. null-scaling branches). Migration:- Code constructing package inputs: change the import — call sites are unchanged.
- Flutter render boundary: convert with
ui.Rect.fromLTRB(r.left, r.top, r.right, r.bottom)and the reverse (copy-paste extensions in the README's Platform Support section).
- Debug logging is opt-in.
BlockClassifierServiceandDriftTrackertake adebugLogger: void Function(String)?constructor parameter (default null = silent) instead of calling Flutter'sdebugPrint. PassdebugLogger: printto restore the previous output. - pubspec surface: the
flutterSDK dependency andflutter: '>=3.19.0'environment constraint are gone; dev-deps aretest+lints(replacingflutter_test+flutter_lints— the effective lint rule set is unchanged,flutter_lintslayered Flutter-widget rules this package never triggered).
Internal #
- CI runs on
dartnatively: latest stable plus a Dart 3.3 floor leg (replacing the Flutter 3.19 floor leg — same floor, expressed in the SDK that now matters). Coverage artifact retained. - All 520 tests pass under plain
dart teston both legs; zero Flutter packages in dependency resolution.
0.7.0 - 2026-07-22 #
Additive release: the opt-in agreementWeighted position-merge
prototype (#58). Default behavior is unchanged — ^0.6.0 consumers can
upgrade without review.
Added #
PositionMergeModelenum andStabilizationEngine(positionMergeModel: ...)(#58). The defaultlegacypreserves 0.x numerics exactly. The opt-inagreementWeightedprototype addresses the audit §1.7 findings:- Merge weight decays with observation count
(
fresh / (existing·n + fresh)): long-observed blocks become positionally sticky — a 6-times-confirmed block barely moves for a single 12px outlier — while young blocks still adapt quickly. - Merged confidence is a running mean of positional agreement
(residual vs the region's drift margin, median-block-height scaled
when no margin exists) instead of the saturating sum: disagreeing
observations now reduce confidence, making
qualityScore's position term informative again for well-observed blocks. Rollout mirrors the band-fallback pattern: ship onlegacy, A/BagreementWeightedagainst your captures, adopt when the numbers hold. Slated to become the 1.0 default pending that validation.
- Merge weight decays with observation count
(
0.6.1 - 2026-07-21 #
Performance release finishing the remaining #55 items. No API changes.
Performance #
- Intra-batch NMS now resolves overlaps against a per-batch spatial grid instead of linearly scanning the whole output per fresh block — ~O(n²)·(drift-margin per pair) becomes O(cells) (#55). Behavioral nuance: the grid applies the same 3×3-neighborhood locality contract the inter-capture matching path already uses, so two blocks whose centers sit more than one bucket apart are no longer compared — only observable for blocks wider than ~2 buckets, which the spatial index documented as out-of-contract in 0.5.1.
stabilize()reuses that batch grid for grouping-contradiction detection instead of building a second throwaway index every capture (#55). The publicdetectGroupingContradictionsnow sizes its temporary index's buckets from the engine's spatial index rather than defaults.DriftTracker.medianDriftForKey/medianBlockHeightForKey/driftMarginForKeycache per-key results, invalidated onaddObservationand every clearing path — previously each call copied and sorted up to 20 samples, several times per block per capture (#55).
Internal #
- CI uploads the lcov coverage report as a build artifact from the latest-stable leg (#60; badge/coverage-service wiring still open there — needs a repo token).
flutter_lintsconstraint comment updated for the Dependabot-widened>=4.0.0 <7.0.0range (#63): 4.x resolves on the Dart 3.3 floor, newer lines elsewhere.- Test count: 513.
0.6.0 - 2026-07-20 #
Audit-driven feature-and-fix release implementing the 0.6.0 roadmap (#46–#56). Pre-1.0, behavioral changes take a minor bump: review the Breaking section before upgrading from 0.5.x.
Breaking #
CssSubmapMembership.spaceKeyFormaps viewport-relative (weight 40) and nested IC+carousel (weight 30) blocks toSpaceKey.unknown()instead ofSpaceKey.normal(...)(#48). Drift observation and correction are now symmetric: aposition:fixedheader no longer receives the page-scroll submap's median correction it never contributed to. Consumers relying on VR blocks being drift-corrected (unlikely — the correction was categorically wrong) must supply a customSubmapMembership.ContradictionEvent's constructor is no longerconstand throwsArgumentErrorwhenevidencehas fewer than 2 entries (#51) — the storage-invariant policy (throw, not assert) now applies here too. Migration: dropconstfrom anyContradictionEventconstructions (engine-produced events are unaffected).- Contradiction detectors skip the viewport-relative boundary (#49):
detectGroupingContradictionsignores VR cached blocks anddetectSplittingContradictionsignores VR fresh blocks. Near scroll offset 0, healthy sticky headers were reported as "subdivided" by unrelated normal blocks and evicted by consumers. - Batch-NMS key lifecycle (#50): dedup keys register only for blocks that survive intra-batch NMS, and an evicted block's key retires with it. Dropped blocks no longer fuzzy-suppress later same-neighborhood blocks. Eviction lookup is identity-based — Equatable-style consumer blocks can no longer cause the wrong value-equal element to be replaced.
- New direct dependency
meta: ^1.11.0; dev-dependencyflutter_lintsmoved^5.0.0→^4.0.0because 5.x requires Dart 3.5 and never actually resolved on this package's declared^3.3.0floor — caught by the new CI floor leg (#56).
Added #
StabilizationEngine.missedFrameRetention(#46): opt-in retention window keeping unmatched cached blocks matchable for N furtherstabilize()calls, so a single OCR miss (glare, occlusion) no longer resets a block's accumulated identity. Default0preserves 0.5.x behavior exactly; retained blocks are never part ofstableBlocks. Thestabilize()index-ownership docs now tell one consistent story.StabilizationEngine.updateViewport(...)(#52): single validated entry point that recomputes the spatial index's adaptive buckets and adopts the same dimensions for dedup keys. ThebucketWidth/bucketHeight/scalesetters now throwArgumentErroron non-finite or non-positive values.DriftTracker.propagationCountFor(spaceKey)— public reader for the counts written viarecordPropagation(#53).TextVoteimplements==/hashCode/toString, matching the package's other value types (#53).BandFallbackStatsInternalis annotated@internal— the analyzer now flags downcast-mutation from outside the package (#53).
Deprecated #
DriftTracker.spaceKeys— use the identicalobservedKeys; removal planned for 1.0 (#53).
Fixed #
DriftTracker.clearKey/clearSpatialRegionnow also clear the matching propagation counts, which previously leaked for the rest of the session (#55).
Performance #
- Text-similarity calls (
isTextSimilar,isTextSimilarWithScores,computeTextSimilarity) extract each string's significant-char list once and feed both metric cores — previously up to 6 extractions per comparison (#55).
Internal #
- CI matrix now tests the declared minimum floor (Flutter 3.19.6) alongside latest stable (#56); Dependabot covers GitHub Actions and pub (#60).
- Band admission ordering caveat and
String.hashCodekey-stability caveat documented (#53). - Test backfill (#54): first dedicated suites for
RobustStats,IqrOutlier,OverlapResolver,BlockKeyGenerator,AbsoluteRect, hierarchy/scroll value types,CssSubmapMembership, andTextVote; real mixed-confidence coverage forcomputeTextConfidence; regression tests for every fix above. Test count: 504 (was 297), verified on both stable and Flutter 3.19.6.
0.5.1 - 2026-07-20 #
Bug-fix release driven by the v0.5.0 package audit
(doc/audit/2026-07-20-package-audit.md in the repository). No API
changes — ^0.5.0 consumers resolve 0.5.1 automatically.
Fixed #
SpatialBlockIndex.candidates()now deduplicates yielded blocks by object identity, matchingallBlocks/blocksInRegion. Previously a dual-indexed IC block reachable from both its page-absolute cell and itsic:cell was yielded twice to an IC query — running the full Levenshtein comparison twice per duplicate and double-ticking theBandFallbackStatsfunnel counters (candidatesConsidered,rejectedSpatial,rejectedTextBand, and in observeOnlybandMatchesIdentified) that consumers are told to read before flipping toadmit.StabilizationEngineprimary matching no longer drops a candidate admitted purely through the Jaccard arm with a Levenshtein score of 0.0 (full character reordering, e.g. a two-character OCR segment swap like北京→京北). The best-candidate scan seeded its comparison at 0.0 with a strict>, so such a match never registered and the observation spawned a duplicate block instead of merging.OcrBlocknow stores a NaNconfidenceasnull(unavailable). The documented clamp could not contain NaN — in IEEE-754,nan.clamp(0.0, 1.0)returns NaN — so NaN leaked to any consumer readingconfidencedirectly. Infinite values still clamp to the range bounds.- Unified the package's two divergent CJK-ideograph definitions into one
shared predicate (
lib/src/internal/cjk_ideographs.dart). The dedup utilities (cjkOnly,cjkFraction, significant-char extraction) omitted CJK Extension B (U+20000–U+2A6DF) while the confidence heuristic included it, so text consisting only of Extension-B ideographs produced an empty significant-char list and could never text-dedup.TextDedupUtilsandisCjkIdeographnow agree.
Documentation #
StabilizationEngine.stabilizedocuments the index-rebuild limitation: blocks not re-observed in a capture leave the spatial index and re-enter as new; app-inserted blocks are dropped at the next call. The cache-merge redesign is tracked for 0.6.0.classifyGroupsdocuments its silent fallbacks (degenerateimageToLayoutScale→ identity CSS-per-px; near-singular container transform → untransformed rect) and the actualpositionLookupthrow contract (caught, neutral stability — previously documented as "must not throw").SpatialBlockIndex.blocksInRegiondocuments the center-cell + 1-cell-margin lookup limit for oversized blocks;remove()documents that cell keys are recomputed from the current rect, so removal after mutation silently no-ops.TextDedupUtils.containmentRatiodocuments the 5,000-rune LCS truncation on the public API (was only on the private helper) and drops a stale internal-caller claim.RobustStats.madOrFallbackscopes its "never zero or negative" guarantee to positiveminSpread.MergeResult.observationCountdocuments the provisional-freeze exception (count passes through unchanged while frozen).
Internal #
.pubignoreadded: internal process docs (doc/superpowers/,doc/audit/) no longer ship in the published package archive.- Full package audit report at
doc/audit/2026-07-20-package-audit.md(repository only). - First dedicated test file for
TextDedupUtils(test/text_dedup_utils_test.dart). Test count: 297 (was 277).
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