Skip to content

batch: Preserve exact state across layered replay - #458

Merged
halfline merged 237 commits into
mainfrom
publish/exact-layered-replay-20260902T154326Z
Sep 2, 2026
Merged

batch: Preserve exact state across layered replay#458
halfline merged 237 commits into
mainfrom
publish/exact-layered-replay-20260902T154326Z

Conversation

@halfline

@halfline halfline commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Saved batches can preserve line-level additions, deletions, and replacements,
then replay that ownership while the index and worktree continue to evolve.
The existing merge paths use recorded source ranges, target ranges, and nearby
content to recover those changes.

Repeated text, nested replacements, whole-file transforms, and applied batch
layers can make several locations look equally plausible. Coordinate evidence
may also become stale after a peel, sift, review, or same-file rewrite. Those
ambiguities can widen a selection, restore unrelated baseline text, duplicate
content, reject a valid operation, or display IDs that no longer identify the
reviewed lines.

This pull request carries exact source and target identity through comparison,
ownership, source advancement, merge, discard, and review workflows. It adds
reciprocal line projections and bounded occurrence indexes, persists complete
file-pair and replacement alternatives, records exact preimages and separator
provenance for applied overlays, and retains exact page membership for file
reviews. Replay now resolves alternatives from their recorded geometry and
verified context instead of borrowing authority from similar surrounding
content.

The regression corpus exercises nested and repeated alternatives, transformed
added files, tracked and untracked whole-file replacements, layered Markdown
rewrites, sifting, reset, sequential undo, and insertions or deletions crossing
evolved source context. Supporting workflow changes keep rich prompt status in
a nonblocking validated cache, apply ordered file-pattern exclusions, preserve
explicit replacement and executable-file behavior, and scope rewrite
publication checks to the movable range. Documentation, checker policy, source
layout, and all maintained translation catalogs match the resulting command
surface.

This pull request supersedes #457. Its transformed-addition fixes are preserved
at the start of the series and extended with exact replacement-origin
projection through the later replay pipeline.

Validation:

  • uv run pytest -n auto
  • uv run ruff check src tests scripts
  • uv run python scripts/check_translations.py
  • uv run python scripts/check_dead_code.py
  • uv run python scripts/check_type_hygiene.py
  • uv run mypy

The discard replacement workflow saves selected replacement text in a batch
and reverses that selection from the live worktree.

Users lose transformed wording when a selected addition extends beyond the
deleted side of a mixed replacement. The rollback treats that wording as an
independent insertion even though it belongs to the replacement already in
progress.

This commit begins preserving transformed additions by recognizing a selected
addition in a mixed replacement run and excluding its rewritten row from the
live-worktree rollback.

The next commit will exercise the retained wording before later commits
preserve its selectable identity and exact selection scope.
The discard replacement workflow retains transformed additions that extend
mixed replacement runs in the live worktree.

The functional suite does not demonstrate the multiline Markdown shape that
exposed the rollback failure. A regression could therefore remove neighboring
replacement content without detection.

This commit continues validating transformed additions with a selected
sentence beyond the deleted side of a replacement and verifies that the
surrounding list remains live.

Subsequent commits will preserve selected wording when it matches deleted
baseline text and when it occupies part of a larger added side.
The discard replacement workflow retains transformed trailing additions by
mapping their rewritten diff rows into saved batch ownership.

Git can align transformed wording with a deleted baseline line and render the
new occurrence as unchanged context. Users then cannot save the replacement
because the rewritten occurrence has no selectable addition row.

This commit continues preserving transformed additions by masking the
rewritten span while Git constructs the diff and restoring its exact bytes on
the resulting addition rows.

Subsequent commits will exercise that alignment case before preserving exact
added-line scope within larger replacements.
The discard replacement workflow retains transformed trailing additions even
when Git could align their wording with deleted baseline content.

The functional suite does not lock down that alignment case across batch
creation and later application. A regression could make the transformed
occurrence unselectable or alter the saved alternative during replay.

This commit continues regression validation by reproducing the baseline-text
collision and asserting the live file, saved batch, and replayed result.

The next code commit will preserve selected added lines that occupy only part
of a larger replacement run.
The discard replacement workflow retains transformed trailing additions and
keeps their rewritten rows selectable when the wording matches baseline text.

Users who transform selected added lines inside a larger replacement can still
lose neighboring working text because the fallback widens those lines to the
entire replacement.

This commit addresses that by keeping a non-empty selection bound to its
resolved worktree coordinates when unselected added lines remain in the same
replacement. The existing snapshot-bound buffer builder now performs that
rewrite, so its dead-code exception is no longer needed.

The final commit will exercise the full document shape, line-count changes,
the complete-added-side boundary, and saved-batch replay.
The discard replacement workflow keeps a non-empty selection of added lines
bound to its worktree coordinates when other added lines remain in the same
replacement. Selecting every added line retains the whole-replacement
behavior.

The functional suite lacks the full document shape that exposed selection
widening. It also does not pin line-count changes or the complete added side
across later batch application.

This commit completes regression validation by reproducing the 31-line
replacement, exercising selections that grow or shrink, and asserting the
complete-added-side behavior through batch creation and replay.

Discarding transformed added lines now preserves their intended scope across
ordinary wording, baseline matches, and partial added-side selections.
Line matching owns two mapped vectors that record source-to-target and
target-to-source correspondences.

The replacement-origin fix needs the matcher result in its reciprocal
orientation, but copying those potentially large vectors would violate the
bounded-memory design.

This commit begins correcting stale replacement origins by allowing a
mapping to transfer its vectors into a reversed owner.

Subsequent commits will add snapshot-bound projections before applying them
to discard workflows.
Line mappings can transfer their mapped vectors into the reciprocal
orientation without allocating duplicate storage.

The new ownership rule must close the original mapping so two objects
cannot claim the same mapped resources.

This commit records the reciprocal lookup results and the closed state of
the original owner.

Later tests will exercise the snapshot-bound projections that consume the
reversed mapping.
Coordinate transforms distinguish snapshot roles even when two snapshots
contain identical bytes.

Replacement origins need to move between such roles without reconstructing
provenance from line matching.

This commit continues the stale-origin fix by adding an exact projection
that verifies identical snapshot content before rebinding a span.

Subsequent commits will add structural projection for evolved sources and
recorded lineage for rewritten worktrees.
Identical snapshots can now rebind coordinates across semantic roles
through an exact span projection.

The projection must preserve the full span geometry without relying on
structural matching.

This commit records the role-rebinding behavior for a snapshot with
identical content identity and extent.

The next code commit will provide conservative span projection when the
batch source has evolved.
Structural alignments expose line correspondences as evidence without
treating them as recorded provenance.

A replacement origin may still be moved safely when every line in its
source span maps reciprocally into one contiguous target span.

This commit adds that conservative span projection while rejecting empty,
fragmented, unmapped, or stale-snapshot requests.

Subsequent commits will expose the typed replacement-origin contract that
consumes the projection.
Structural alignments can now project a requested span only when its mapped
lines form one exact target range.

The boundary needs tests for successful relocation, fragmented matches,
unrelated global ambiguity, and stale snapshot evidence.

This commit records the accepted contiguous cases and the conservative
rejection cases.

The next code commit will bind recorded rewritten-worktree lineage to an
exact transform.
Recorded batch-source lineage has typed exact transforms for original
source coordinates and observed worktree coordinates.

The transformed discard workflow produces another coordinate role after
applying its explicit edit, so ordinary worktree typing cannot describe
that source snapshot.

This commit adds a dedicated rewritten-worktree lineage variant with
runtime role validation.

Subsequent commits will use the available transforms through a typed
replacement-origin interface.
Replacement-origin inputs describe where semantic replacement runs came
from, but they do not describe the current batch-source snapshot that will
store them.

Passing untyped line-number callbacks would permit the coordinate-space
mix-up that caused stale replacement spans.

This commit defines a generic snapshot-bound projection protocol whose
target is always batch-source space.

Later commits will bind replacement metadata through that protocol before
discard workflows adopt it.
Replacement-unit origins persist produced-side geometry as line numbers in
the current batch source.

Callers that translate a typed source span still have to unpack it
manually, which could discard the proof of its coordinate role at the
assignment boundary.

This commit adds a model operation that accepts only a batch-source
snapshot span before updating the stored geometry.

Subsequent commits will route live hunk origins through the typed
projection contract.
Live hunk replacement translation derives origin geometry from the
replacement stream that is currently displayed.

After a batch source evolves, those displayed line numbers can name a
different range or extend beyond the source snapshot.

This commit projects each complete semantic origin span into batch-source
coordinates and omits origin evidence when the mapping is not exact.

Subsequent commits will expose that behavior through the selection
translator before each discard workflow supplies its source projection.
Live hunk translation can now bind semantic replacement origins to a
contiguous batch-source span.

The behavior needs to distinguish an exact relocated range from content
that appears only in fragmented source positions.

This commit records the rebound coordinates for the exact case and omitted
provenance for the fragmented case.

The next code commit will carry the projection through the higher-level
hunk selection translator.
The low-level replacement translator accepts a snapshot-bound origin
projection, while the general hunk selection entry point still exposes only
replacement content.

Discard workflows enter through the general translator, so they cannot yet
preserve the typed coordinate evidence.

This commit threads the generic projection through that entry point without
changing callers that omit it.

Subsequent commits will construct the projection for ordinary stale-source
discard selections.
Batch ownership updates can carry a typed origin projection through hunk
translation, but the update acquisition layer does not construct one from
live source content.

Ordinary discard selections may use either unchanged source bytes or a
source advanced by prior batch operations.

This commit prepares an exact same-content projection or a reciprocal
structural alignment when a caller supplies the live replacement target.

The next code commit will make the ordinary line discard path supply that
target.
Line-based batch updates prepare replacement runs from the current working
content and can now construct a batch-source projection for their origins.

The discard path does not supply that content to ownership acquisition, so
cached sources can still persist origin coordinates from the wrong
snapshot.

This commit enables projection for ordinary line discard by passing the
live working-source buffer into ownership acquisition.

Subsequent commits will record the cached-source reproducer before applying
the same invariant to transformed discard selections.
Ordinary line discard now projects replacement origins from the live
working snapshot into the current batch source.

A prior deletion peel can enlarge the working file after the later batch
source was cached, which previously left the next replacement with
out-of-range origin geometry.

This commit reproduces that sequence and verifies both the stored source
bounds and the resulting batch content.

The remaining commits will apply the same coordinate invariant to the
transformed replacement discard workflow.
Ordinary line discard now stores replacement origins in current
batch-source coordinates, while transformed replacement discard still
records rewritten display coordinates directly.

Separate peels can evolve the source around those replacements, so the
transformed path can restore an unrelated baseline span even when its
numeric range remains in bounds.

This commit composes the recorded edit with exact rewritten lineage for
normal replacements and expanded semantic parents before binding their
origins.

The final commit will preserve the fuller multi-peel reproducer that closes
the stale replacement-origin fix.
Both ordinary and transformed discard workflows now bind replacement
origins to the batch-source snapshot that persists them.

The fuller failure requires three separate peels from an evolving Markdown
replacement, where stale numeric geometry previously restored an earlier
baseline block.

This commit reproduces that sequence and verifies that the final batch
retains each intended replacement in file order.

The series now prevents replacement-origin coordinate mix-ups through
snapshot-typed projections across both discard workflows.
Line comparison streams semantic change runs as range-backed records. Each
record classifies an unmatched gap and retains its source and target ranges
with the preceding target line.

Saved and live replacements can be layered across snapshots where the same
numeric positions identify different content. The program cannot replay
those layers exactly unless comparison preserves positional evidence for
both sides of every changed section.

This commit begins making layered replacement replay exact by adding the
preceding source line to every streamed range record beside its existing
target anchor.

The next commit will exercise streamed range anchors directly. Subsequent
commits will add exact-sequence groundwork, then carry that evidence
through replacement ownership into saved and live replay.
Line comparison streams semantic changes as range records that retain the
source and target positions immediately before each changed section.

The project does not directly verify that the newly retained source
position survives streaming across replacements, deletions, and additions.
Exact layered replay could lose needed positional evidence without a
comparison test detecting the regression.

This commit exercises the source anchor, meaning the source position before
a change, on streamed replacement, deletion, and addition range records.

Subsequent commits will add exact-sequence groundwork, then propagate the
anchor evidence through replacement ownership into saved and live replay.
Line comparison streams semantic change ranges with the source and target
positions immediately before each changed section. Direct range tests
exercise those anchors across replacements, deletions, and additions.

The project lacks a bounded search for every byte-for-byte occurrence of a
line sequence, including overlapping occurrences, without retaining
temporary match state in Python containers. Replacement ownership needs
that capability to locate corresponding content across layered snapshots.

This commit adds a linear-time iterator for exact sequence occurrences
within optional source bounds. It stores prefix-match lengths in a matcher
workspace and releases that scratch resource when iteration finishes or
callers stop early.

Next commits will add reciprocal mapping exclusions and structural
occurrence matching before replacement ownership consumes the search.
Line comparison provides reciprocal mappings between batch-source and
working-tree lines, and exact-sequence search keeps its match workspace
outside the Python heap.

The project lacks a way to retain those relationships while omitting
selected source lines and target spans without growing Python heap use.
Structural occurrence matching needs that filtered evidence before it can
normalize repeated content for replacement ownership.

This commit adds a mapping copy operation that walks the target side once,
skips pairs covered by either exclusion, and rebuilds both directions in
mapped storage. It validates target spans, preserves uncertainty about
unmapped equal lines, and closes allocated storage after failures.

The preceding commit established exact-sequence search with scratch storage
outside the Python heap. Filtering reciprocal mappings under the same
constraint advances that groundwork; the next commit can normalize
structural occurrences before replacement ownership uses them.
Line comparison can copy reciprocal mappings while excluding selected
batch-source line ranges and working-tree target spans. The copy retains
both mapping directions in mapped storage and marks excluded pairs as
potentially equal but unmapped.

The project does not directly verify that exclusions clear each reciprocal
entry or that a large target span stays represented by fixed boundary
offsets. A regression could leave one direction stale or return line-scale
temporary storage to Python heap memory.

This commit exercises source-range and target-span exclusions, checks both
directions and the uncertainty flag, and bounds Python heap growth as the
excluded span expands.

The preceding commit introduced the filtered mapping copy. The next commit
will normalize structural occurrences before replacement ownership consumes
the retained positional evidence.
Line comparison can retain mappings in both directions outside excluded
source and target regions. Its mapped-storage occurrence index groups exact
or line-ending-normalized byte payloads.

Structural occurrence matching must recognize lines after nesting shifts
them to another indentation level. Payload identity preserves leading
spaces and tabs, while applying byte indentation rules to caller-defined
non-byte payloads would give that mode no valid meaning.

This commit continues preparing structural occurrence matching by adding an
indentation-insensitive occurrence index mode. Normalized payloads lose
their final line terminator before leading spaces and tabs are stripped,
and the mode rejects non-byte payloads rather than applying byte
indentation rules.

The next commit will exercise indentation-insensitive occurrences before
later work searches for occurrences bounded by adjacent lines.
Line comparison has an occurrence index that groups repeated line payloads
after normalizing their final line endings. Callers can also request that
leading spaces and tabs be ignored for byte payloads.

The project does not directly verify that the optional mode normalizes both
indexed lines and lookup values. A regression could match content at its
original indentation while losing it when the same structural line moves
into another block.

This commit exercises indentation-insensitive occurrence counts with tabs,
spaces, and differing line endings on both sides of a lookup. The checks
show that normalized structural payloads remain identical across
indentation shifts.

The next commit will search for structural occurrences bounded by adjacent
lines.
Functional discard coverage applies one transformed paragraph replacement
at a time.

It does not demonstrate sequential replacements whose headings and shared
prose evolve between batch operations.

This commit records and discards two Markdown replacements while checking
each retained section and boundary.

Replacement discard now has end-to-end coverage for sequential document
rewrites.
Functional apply coverage replays line-oriented edits across batch layers.

It does not combine independent word-level insertions around a paragraph
rewritten by another layer.

This commit applies layered paragraph replacements and verifies compatible
words from each layer survive once.

Applied-text replay now has end-to-end coverage for layered paragraph
composition.
Functional apply coverage composes transformed text before ownership is
recorded.

It does not replay a later paragraph transformation after an earlier batch
has already established ownership.

This commit records ownership first, applies a transformed paragraph, and
verifies baseline words and both compatible insertions.

Applied-text replay now has end-to-end coverage after ownership capture.
Whole-file discard planning replays ownership and applied overlays against
the current worktree.

When the selected batch is the trailing application, that reconstruction
can guess through a transformed file even though its exact predecessor was
recorded.

This commit loads the saved predecessor for unscoped trailing-batch discard
before falling back to structural reversal.

Discarding a trailing whole-file batch now restores its exact prior
content.
Functional apply coverage replays a whole tracked file without earlier
batch removals.

It does not verify that surviving baseline deletions remain absent after a
prior batch peels independent content.

This commit adds that sequence and asserts the exact whole-file result.

Whole-file replay now has end-to-end coverage after an independent peel.
Functional replacement coverage extends a whole-file transform before
independent batch content is removed.

It does not repeat the extension after that separate content has already
been peeled from the live file.

This commit updates the scenario to verify the extended transform keeps the
peeled content absent.

Whole-file replacement now has end-to-end coverage after an independent
peel.
Presence-context tests cover a direct recorded alternative.

They do not distinguish exact claim elision from wrong duplicate
coordinates or ambiguous enclosing target spans.

This commit adds accepted and rejected mappings for those alternative
placements.

Presence context now has direct coverage for exact and unique live
alternatives.
Structural validation tests cover complete replacement mappings.

They do not stress split claimed ranges, a claimed line matching its old
side, or unrelated source matches inside the replacement old span.

This commit adds cases that require replacement ownership to rebuild and
remove content only within its verified boundary.

Replacement validation now has direct coverage for fragmented and duplicate
text.
Merge tests cover one source alternative positioned by distinct surrounding
content.

They do not stress leading separators, duplicate old-side text outside the
selected span, or several unambiguous predecessor occurrences.

This commit adds replay cases that require source-scoped alternative
placement and exact separator restoration.

Structural merge now has direct coverage for duplicate and repeated
alternatives.
Functional discard coverage uses focused synthetic transformations and a
smaller set of realistic source movements.

It does not cover the broader combinations of predecessor restoration,
adjacent deletion geometry, duplicate anchors, whole-file expansion, and
retained suffix placement.

This commit adds a regression corpus for those transformed replacement
workflows and updates the shared scenarios to assert exact replay
boundaries.

Discard and apply replay now have broad end-to-end coverage for composed
real-world transformations.
Functional inclusion coverage places tracked insertions after distinct
context.

It does not isolate an insertion whose boundary follows a closing fence
shared by neighboring transformed content.

This commit includes that insertion and verifies its exact position and
surrounding text.

Tracked insertion replay now has end-to-end coverage for shared closing
boundaries.
Live-change workers import the applied overlay view through a data-layer
compatibility module.

The view now belongs to the batch layer, so retaining the indirect import
obscures ownership and keeps an unnecessary dependency edge.

This commit imports the batch overlay view directly while normalizing the
worker module layout.

Live status counting now follows the current batch-layer boundary.
Recent implementation work leaves touched modules with older wrapping,
dense expressions, and authority-oriented module descriptions.

The mixed presentation makes the edited paths inconsistent even though
their behavior is already covered by separate commits.

This commit applies the current local normalization to source and benchmark
modules without changing their runtime decisions.

The touched implementation paths now use consistent prose and layout.
The expanded regression suite leaves several touched test modules using
older wrapping and spacing.

Those presentation-only differences obscure the behavioral assertions
committed in the preceding validation changes.

This commit normalizes imports, expressions, and blank-line layout without
changing test intent.

The touched tests now follow one consistent source layout.
The Arabic message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Arabic translations.

The Arabic catalog now matches the translatable application surface.
The Czech message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Czech translations.

The Czech catalog now matches the translatable application surface.
The German message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
German translations.

The German catalog now matches the translatable application surface.
The Spanish message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Spanish translations.

The Spanish catalog now matches the translatable application surface.
The French message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
French translations.

The French catalog now matches the translatable application surface.
The Japanese message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Japanese translations.

The Japanese catalog now matches the translatable application surface.
The Korean message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Korean translations.

The Korean catalog now matches the translatable application surface.
The Dutch message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Dutch translations.

The Dutch catalog now matches the translatable application surface.
The Polish message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Polish translations.

The Polish catalog now matches the translatable application surface.
The Brazilian Portuguese message catalog predates the latest include
diagnostics and replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Brazilian Portuguese translations.

The Brazilian Portuguese catalog now matches the translatable application
surface.
The Russian message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Russian translations.

The Russian catalog now matches the translatable application surface.
The Turkish message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Turkish translations.

The Turkish catalog now matches the translatable application surface.
The Ukrainian message catalog predates the latest include diagnostics and
replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Ukrainian translations.

The Ukrainian catalog now matches the translatable application surface.
The Simplified Chinese message catalog predates the latest include
diagnostics and replacement-batching status text.

Leaving it unchanged would omit the new source messages and retain obsolete
reference ordering after the implementation series.

This commit merges the current message inventory and supplies the updated
Simplified Chinese translations.

The Simplified Chinese catalog now matches the translatable application
surface.
@halfline
halfline merged commit fc65c5c into main Sep 2, 2026
11 of 12 checks passed
@halfline
halfline deleted the publish/exact-layered-replay-20260902T154326Z branch September 2, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant