Finish nested-list indentation and movement behavior #17
Open
opened 2026-09-13 13:34:44 +00:00 by kayg
·
26 comments
No Branch/Tag specified
dev
wave19/desktop-test
wave19/per-space-state
wave19/first-space-integration
wave19/first-space-draft-transfer
wave19/block-editor-craft
codex/wip/root-conflict-snapshot-20260914
wave12/first-space-draft-transfer
wave14/editor-overnight-combined
wave17/editor-diagnostics-3bbb
wave16/code-block-polish
wave16/image-controls
wave16/event-title-crash-control
wave16/image-preview-dispatch-repair
wave16/event-title-lifecycle-qa
wave16/editor-list-refinements
wave17/editor-layout-correction
wave16/event-edit-date
wave16/note-title-presentation
wave16/editor-final-combined
wave18/document-chrome-repair
wave17/cross-event-drop
wave16/editor-structural-combined
wave16/editor-hover-margins
wave16/editor-caret-relocation-perf
wave16/inspector-polish
wave16/image-preview-integration
wave16/english-transcription
wave16/document-background-edge
wave16/sidebar-live-layout
wave15/image-preview-ownership-repair
wave15/editor-handle-six-dot
wave15/titlebar-public-repair
wave15/sidebar-initial-restoration
wave15/editor-formatting-tooltips
wave15/editor-drag-mounted-repair
wave15/image-preview-foundation-isolated
wave15/editor-block-drag-feedback
wave15/sidebar-attachment-layout
wave14/editor-inline-plain-fastpath
wave14/editor-rendered-paste-replacement
wave14/clipboard-awaited-shutdown
wave14/editor-selectall-toolbar-perf
fix/clipboard-body-undo-group
diagnostic/clipboard-insertion-491
wave14/sidebar-unselected-drag-v2
diagnostic/sidebar-unselected-drag-trace
diagnostic/clipboard-phase-0225
wave14/sidebar-prime-access
wave14/nested-projection-handle-test-fix
wave14/sidebar-unselected-event-drag
fix/clipboard-task-ownership-6318
fix/editor-resource-url-invalidation
codex/wip/editor-handle-render-diagnostic-20260914
fix/central9c9-clipboard-crash
codex/wip/editor-handle-diagnostic-20260914
wave14/sidebar-event-runtime-repair
wave14/nested-projection-runtime-fix
wave14/sidebar-event-test-fixtures
wave14/sidebar-attachment-overlap
integration/rendered-image-clipboard
wave14/sidebar-event-preview-integration
wave14/editor-nested-list-integrate
wave14/editor-nested-list-projection
task/rendered-image-clipboard
wave14/sidebar-paste-preview-integration
wave14/sidebar-event-day-dnd
wave14/editor-preview-next
wave14/editor-list-handle-integrated
task/native-basic-paste
wave14/editor-list-keyboard-audit
task/native-clipboard-contract
codex/wip/editor-handle-geometry-20260914
wave14/editor-block-drag
wave14/editor-drag-integrated
wave14/editor-empty-marker-map-publication
wave14/editor-empty-marker-runtime-diag
wave14/editor-empty-marker-preview
wave14/editor-empty-marker-host-repro
wave14/editor-empty-marker-probe
codex/wip/editor-hr-leaf-fix-20260914
wave14/event-attachment-drop
wave14/editor-empty-marker-paint
wave14/editor-drop-list-preview
wave14/editor-finder-drop
wave14/editor-rule-caret
wave14/editor-list-enter-next
wave14/editor-image-qa
wave14/editor-mode-commands
wave13/editor-code-performance-preview
wave14/sidebar-overlap-repair
wave13/cross-day-compile-repair
wave13/spotlight-retry
wave13/editor-chrome-runtime-repair
wave13/editor-inline-fast-path
codex/wip/detached-87aa-20260914
wave12/editor-viewport-polish
wave12/craft-code-block-polish
wave12/editor-asset-return-preview
wave12/voice-polish-preview
wave12/editor-native-return-runtime-repair
wave12/editor-asset-focus
wave12/cross-day-reviewed-integration
wave12/optional-voice-provider-finish
wave12/event-move-descriptor-compile-fix
wave12/editor-image-geometry-preview
wave12/editor-native-return-undo-repair
wave12/image-native-geometry-fixture
wave12/editor-pattern-preview
wave12/editor-bullet-continuation
wave12/editor-top-chrome
wave12/voice-composer-restart
wave12/speech-variable-buffer-repair
wave12/editor-image-preview
wave12/event-move-descriptor-scale
wave12/editor-image-geometry
wave12/afternoon-ui-preview
wave12/sidebar-hosted-bounds-repair
wave12/sidebar-overlap-live
wave12/speech-e2e-verification
wave12/mentions-polish-finish
wave12/editor-structural-range-repair
wave12/sidebar-fade-desktop-preview
wave12/sidebar-design-ledger
wave12/voice-assets-physical-fixtures
wave12/native-event-marker-polish
wave12/sidebar-fade-undo-preview
wave12/event-move-fence-corrections
wave12/sidebar-fade-boundary-repair
wave12/parent-space-entry-integration
wave12/editor-undo-group-repair
wave12/native-event-row-performance
wave12/parent-swipe-integration
wave12/metadata-noteid-test-fix
wave12/space-composer-generation-fix
wave12/voice-lifecycle-clock-fixture
wave12/sidebar-child-binding
wave12/voice-provider-pool-preview
wave12/composer-recording-preview
wave12/event-cross-day-store-corrections
wave12/voice-async-test-locks
wave12/speech-buffer-lease-fix
wave12/composer-no-top-readouts
wave12/sidebar-child-target
wave12/space-required-composer
wave12/editor-correctness-preview
wave12/voice-provider-compile-corrected
wave12/event-move-integration
wave12/voice-assets-compile-fix
wave12/parakeet-compile-fix
wave12/note-metadata-flexibility
wave12/sidebar-event-drag-drop
wave12/editor-paste-source-guard
wave12/note-asset-import-safety-causal-tests
wave12/note-asset-import-safety
wave12/editor-readonly-command-guards
wave12/voice-provider-integration
wave12/editor-format-flexibility
wave12/hosted-sidebar-ui-sdk-fix
wave12/event-cross-day-store
wave12/voice-app-wiring
wave12/voice-parakeet-assets
wave12/voice-hosted-await-fix
wave12/event-edit-date-guard
wave12/event-cross-day-app
wave12/network-client-entitlement
wave12/sidebar-row-overlap
wave12/voice-settings-surface
wave12/parakeet-capture-adapter
wave12/task-marker-normalization
wave12/voice-polish-integration
wave12/voice-capture-hardening
wave12/voice-provider-settings-plan
wave12/copied-space-editor-io
wave12/hosted-background-sendability
wave12/voice-stop-nlp
wave12/voice-fixture-roots
wave12/voice-claim-collision
wave12/sidebar-add-swipe-plan
wave12/copied-space-app-wiring-plan
wave12/voice-fence-stat-compilefix
wave12/composer-dismiss-crash-hotfix
wave12/voice-retainer-defer-fix
wave12/overnight-recovery-space-preview
wave12/background-checkpoint-compilefix
wave12/copied-space-enumeration-fix
wave12/copied-space-local-state
wave12/voice-fence-fixture-fix
wave12/voice-manifest-fd-fix
wave12/voice-recovery-publication-proof
wave12/voice-retainer-bytecount-fix
wave12/overnight-navigation-background-compilefix
wave12/navigation-work-diagnostic
wave12/overnight-navigation-background-preview
wave12/background-import-bounds
wave12/overnight-voice-lifecycle-preview
wave12/voice-frozen-claim
wave12/voice-input-seam
wave12/voice-recovery-compile-fix
wave12/voice-terminal-cleanup-plan
wave12/voice-recovery-activation-plan-current
wave12/overnight-runtime-probe-preview
wave12/overnight-sandbox-startup-preview
wave12/startup-geometry-diagnostic
wave12/voice-source-sandbox-fix
wave12/image-root-sandbox-fix
wave12/perf-scroll-zero-args
wave12/overnight-document-geometry-preview
wave12/native-document-geometry-diagnostic
wave12/overnight-draft-root-preview
wave12/voice-recovery-c-session-fence
wave12/draft-root-direct-open
wave12/hosted-single-module-linkage
wave12/overnight-voice-recovery-preview
wave12/voice-recovery-checkpoint-a-finalization
wave12/copied-space-local-state-plan
wave12/overnight-refresh-hosted-preview
wave12/incremental-refresh-bounded-load
wave12/raw-source-reconciliation-test-fix
wave12/overnight-rendering-startup-preview
wave12/draft-root-diagnostic
wave12/day-event-bulk-load
wave12/speech-asset-install-test-preview
wave12/hr-material-contrast
wave12/height-clamp-followup
wave12/voice-recovery-checkpoint-a-corrections
wave12/overnight-cstore-caret-preview
wave12/voice-recovery-checkpoint-a
wave12/editor-adjacent-caret-boundary-v3
wave12/voice-recovery-index-query-fix
wave12/editor-adjacent-caret-boundary-v2
wave12/voice-recovery-index-query
wave12/startup-collector-empty-args
wave12/editor-adjacent-caret-boundary
wave12/overnight-hr-storage-preview
wave12/hr-native-diagnostic-followup
wave12/hr-native-diagnostic
wave12/overnight-storage-settings-preview
wave12/snapshot-native-move-followup
wave12/speech-local-fallback
wave12/settings-stream-test-compilefix
wave12/snapshot-path-identity-followup
wave12/corefrontmatter-join-fastpath
wave12/speech-fallback-plan
wave12/overnight-caret-control-registry
wave12/editor-registry-compilefix
wave12/voicea-test-compilefix
wave12/overnight-caret-control-ready
wave12/voicea-enum-compilefix
wave12/voice-recovery-phaseb
wave12/overnight-caret-control
wave12/overnight-polish-storage-preview
wave12/voicea-foundation-compilefix
wave12/snapshot-path-identity
wave12/overnight-native-polish-preview
wave12/hr-rule-geometry
wave12/speech-conversion-fixture
wave12/editor-deferred-caret
wave12/overnight-hosted-lifetime-preview
wave12/marker-glyph-clipping
wave12/voice-recovery-activation
wave12/overnight-package-fixture-preview
wave12/overnight-lifetime-voice-preview
wave12/app-tests-cleanup
wave12/snapshot-physical-fixtures
wave12/event-date-title-punctuation
wave12/overnight-lifetime-voice-plan
wave12/overnight-draw-before
wave12/overnight-picker-event-preview
wave12/settings-watcher
wave12/event-date-title-fix
wave12/note-background-picker-fix
wave12/editor-lifetime
wave12/overnight-draw-preview
wave12/snapshot-attribute-compile-fix
wave12/editor-accepted-draw-trace
wave12/overnight-runtime-preview
wave12/composer-test-concurrency-fix
wave12/editor-caret-performance
wave12/overnight-snapshot-correction
wave12/settings-model-callbacks
wave12/overnight-full-preview
wave12/overnight-store-preview
wave12/rendered-bullet-fix
wave12/settings-document-fixture-fix
wave12/composer-task-pill-ui
wave12/overnight-roadmap-preview
wave12/task-tag-readouts
wave12/event-snapshot-dependency
wave12/h1-strict-preview
wave12/settings-store-await-fix
wave12/settings-store-test-fix
wave12/overnight-pill-preview
wave12/composer-pill-accent-fix
wave12/composer-pill-final
wave11/sidebar-cold-index-prime
wave12/overnight-preview
wave11/voice-draft-recovery
wave11/voice-transcription-finish
wave11/note-save-rename
wave11/task-event-preview
wave11/settings-enabled-plugin-section
wave11/pill-fade-preview
wave11/sidebar-top-fade-regression
wave11/event-sidebar-preview
wave11/composer-pill-restore
wave11/settings-plugins-portability
wave11/composer-hash-semantics
wave11/event-composer-edit
wave11/sidebar-spacing-followup
wave11/editor-performance-evidence
wave11/voice-preview
wave11/voice-stop-repair
wave11/polish-verification
wave11/plugins-settings-ui
wave11/bullet-list-regression
wave11/sidebar-insert-stability
wave11/polish-preview
wave11/document-style-repair
wave11/composer-native-readouts
wave11/mentions-disclosure-motion
wave11/sidebar-swipe-compact
wave11/legacy-helper-path-fix
wave11/persistence-integration
wave11/capsule-instant-presentation
wave11/native-link-activation
wave11/desktop-integration
wave11/link-integration
wave11/synthetic-note-names
wave11/composer-overflow-actions
wave11/legacy-root-links
wave11/note-title-punctuation
wave11/note-title-metadata
wave11/spotlight-navigation
wave11/spotlight-trace-baseline
wave11/note-name-collisions
wave11/identity-query-indexes
wave11/note-heading-title
wave11/capsule-scroll-chrome
wave11/sidebar-context-menu
wave11/spotlight-metadata
wave11/rename-link-repair
wave11/analytics-native
wave11/sidebar-scroll-restoration
wave11/sidebar-bottom-spacing
wave11/capsule-optical-alignment
wave11/markdown-link-resolution
wave11/command-results-layout
wave11/capsule-outside-dismissal
wave11/events-folder
wave11/capsule-query-retention
wave11/capsule-snap-preview
wave11/composer-controls-preview
wave11/sidebar-fade-preview
wave11/selection-lookup
wave11/note-creation-routing
wave11/sidebar-header-spacing
wave11/block-editor
wave11/space-onboarding
wave11/space-terminology
wave4/voice
wave2/search
wave4/glur-probe
wave7/cli-integration
wave12/inspector-style
wave4/document-presentation
wave2/cli
wave2/sidebar
No results found.
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal-macos#17
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Request and current status
Empty bullet and up/down movement user-confirmed; simple indentation checked, broader nesting/multiselection unresolved.
Acceptance
Tab/Shift-Tab correctly move intended blocks and descendants. Preserve immediate empty markers and Enter-on-empty exit behavior. Verify ordered/task/bullet mixes, wrapped items, disjoint/range selection, movement and undo with Markdown readback.
Keep open until the user-facing outcome is verified in a delivered build. Source review or a successful build alone is not completion.
Worker
document_title_semantics, source-only audit for issue #17. Current central iswave14/editor-overnight-combined@9e3a64b6b2a23b1ac9e6c10ba13219bda3d32405; checkout is clean. I am reviewing the nested-list command paths and existing source tests for Tab/Shift-Tab, nested movement/subtrees, blank or empty bullets, ordered/task/bullet mixes, and disjoint/range selection. No build, GUI run, checkout, or source edit will be performed. The result will separate source/test-confirmed behavior from unverified gaps and post a bounded repair/acceptance matrix here.Issue #17 nested-list source audit
Snapshot: central
wave14/editor-overnight-combined@9e3a64b6b2a23b1ac9e6c10ba13219bda3d32405; checkout clean. This is a source/test read only. No build, GUI run, or source edit was performed.Confirmed by source and tests
textView(_:doCommandBy:)routesinsertTab/insertBacktabinSources/CalternalEditor/BlockEditorView.swift:2903-2917; responder fallbacks are at2949-2956; physical Tab key handling is at3555-3567and3622-3626.handleListIndentrequires list blocks, removes descendant roots, and edits every physical line in each root subtree (BlockEditorView.swift:3633-3675,3927-3959).testNativeListIndentAndNestedMovePreserveSubtreesSelectionsAndHistorycovers a wrapped continuation moving withalpha, then undo/redo (Tests/CalternalEditorTests/MarkdownBlockDocumentTests.swift:1529-1563).testListTabIsScopedToListsAndFirstItemCanIndentcovers first-item indent, outdent, mixed whitespace, contiguous selected siblings, mixed-depth outdent no-op, and non-list/code Tab pass-through (1640-1718).BlockEditorView.swift:3682-3787). Existing coverage movesbeta/alpha, carriescontinuation, handles a continuation-line caret, and checks mixed line endings/EOF mapping (MarkdownBlockDocumentTests.swift:1565-1638,1720-1782). Root block movement preserves the complete root block/subtree (MarkdownBlockDocument.swift:673-771; test944-952).MarkdownProjection.swift:980-1065).testNestedListLinesKeepPerLineMarkerTaskAndEmptyIdentitySemanticsverifies mixed nested rendering, task source mapping, and the empty marker (MarkdownBlockDocumentTests.swift:2560-2646).BlockEditorView.swift:3048-3110,3113-3219). Tests cover empty bullet/ordered/task source, caret/history, list continuation, and second Return exit (MarkdownBlockDocumentTests.swift:1914-1983,1985-2172,2174-2200).BlockEditorView.swift:1975-2095,2097-2195;MarkdownBlockDocumentTests.swift:1230-1421). This does not establish structural command support.Concrete gaps and limits
Disjoint structural selection is blocked.
doCommandBybeeps and returns before Tab/Shift-Tab whenhasMultipleBlockSelections(BlockEditorView.swift:2903-2908).keyDowndoes the same before either indentation or movement (3555-3567). The directinsertTab/insertBacktabfallback callshandleListIndent, which reads onlyselectedRange()(3633-3643), so it cannot apply the command to all nativeselectedRanges. The existing disjoint-selection test checks copy/delete/cut only; there is no Tab/Shift-Tab or movement assertion.Nested movement is intentionally narrow.
handleNestedListMoveaccepts one top-levelMarkdownBlockonly (3687-3693), then requires every selected root to have the same block, parent, and indentation depth and to be a contiguous sibling span (3698-3713). A mixed-depth selection is consumed as a no-op; it cannot move selected roots as a set. The outdent path similarly consumes any selection containing a depth-zero root without changing the deeper roots (3645-3650), and the existing mixed-depth test records that current no-op (1690-1704). The product rule for mixed-depth movement/outdent needs to be explicit.Blank-line-separated nested items are separate parsed blocks. The parser stops a list block on a blank physical line (
MarkdownBlockDocument.swift:1008-1015). An indented item after that blank is parsed as another top-level list block, so the nested sibling path cannot see it as a sibling; the generic block mover would move the whole parsed blocks instead. There is no test specifying whether the authored blank line is a list boundary (preserve/no-op) or whether nested movement should carry it across.Empty nested bullets are projected and Return-tested, but structural commands are unverified. The regex accepts an empty marker and the projection emits an atomic zero-width anchor, but no test applies Tab, Shift-Tab, nested up/down movement, or undo/Markdown readback to an empty nested bullet/task/ordered item. This is an evidence gap rather than a claimed runtime failure.
Ordered/task/bullet mixes are only projection/Return-tested. The per-line renderer and Return parser recognize the three marker families, but structural Tab/Shift-Tab and sibling movement are tested only with bullet fixtures. Cross-kind siblings, mixed kinds across a selected range, wrapped bodies, and hidden IDs need direct source readback coverage.
Selection normalization is top-level-block-shaped.
discontiguouslySelectedBlocksaccepts only ranges exactly equal todocument.blocksdisplay ranges (1975-1987); nested list lines live inside one root block. A future structural multi-selection path must define whether a nested item is selectable independently, normalize each native range to its outline root, deduplicate descendants, and restore all ranges after the source transaction.Bounded repair plan for the implementation lane
selectedRange().doCommandBy) and actualkeyDownTab/Shift-Tab/Up/Down paths, because current tests mostly call responder actions directly.Acceptance matrix
1529-15631565-1638, EOF/line endings at1720-17821914-1983,2174-2200,2560-2646; structural commands absent1640-1704)1230-1421); structural paths currently beep/primary-onlyMarkdownBlockDocument.swift:1008-1015; no command test1706-1717No tests were executed per the source-only constraint. Completion still requires the delivered-build user verification stated by issue #17.
Implementation start / plan from worker
/root/block_drag_finish./Users/kayg/Developer/calternal-app/.worktrees/editor-list-refinementswave16/editor-list-refinements56e61d2e4d8da6915fc693a99b2d09d8b5160863Sources/CalternalEditor/BlockEditorView.swiftand one focused regression inTests/CalternalEditorTests/MarkdownBlockDocumentTests.swiftonly.Source finding: discontiguous native
selectedRangescurrently beep before Tab/Shift-Tab in both delegate and keyDown routes. Direct responder Tab reacheshandleListIndent, which reads onlyselectedRange()and would mutate only the primary range. Existing single/range list indentation, subtree traversal, mixed-depth no-op, empty-anchor projection, and canonical source-edit helpers are otherwise reusable.Bounded plan for this candidate:
setSelectedRanges. A parent root owns its descendants; repeated descendant selections are edited once.textView(_:doCommandBy:)and physicalkeyDown. Copy/Cut/Delete, movement, paste, formatting, and IME guards remain unchanged in this candidate.Performance scope: outline parsing is limited to the blocks touched by the native ranges; roots and edited physical-line starts are deduplicated. No full-document scan is introduced. No local build or VM run; plan review by
/root/editor_polish_inventoryis the next gate.Reviewer:
/root/editor_polish_inventoryPlan reviewed against
/tmp/calternal_issue17_nested_bullet_audit_20260913.md, the latest issue comment, and exact base56e61d2e4d8da6915fc693a99b2d09d8b5160863in.worktrees/editor-list-refinements.Verdict: revise the plan before implementation. No source edits/build/GUI were performed.
P1 — Define and preserve native selection identity, not only mapped ranges
BlockEditorView.swift:1967-1987, 2197-2212, and source-position affinity is4022-4095.blockSelectionAnchorSourceLocation. ExistingmappedSourcePositiondeliberately uses different affinity for range starts, ends, and carets; calling it generically for every endpoint is insufficient.P2 — Reusing current outline and endpoint helpers makes large selections quadratic
listOutlineItemsatBlockEditorView.swift:3797-3848,selectedListOutlineRootsat3927-3959, andmappedSourcePositionat4072-4095; proposed steps 1 and 4.listOutlineItemssearches backward for a parent and forward for each subtree (quadratic in item count). Resolving every range by filtering all items and walking ancestors adds another range×item cost. Mapping two endpoints for every range through every edit is range×edit.P2 — One transaction needs an explicit sparse-edit/envelope cost policy
applyListSourceEditsatBlockEditorView.swift:4022-4069.P2 — The test must distinguish reachable whole-block selection from programmatic nested ranges
BlockEditorView.swift:2097-2195.MarkdownBlockranges; nested list items live inside one such block. A test that directly injects discontiguous nested-line ranges can pass while users cannot create that state, or it can establish semantics different from selecting the whole list block (which resolves to all depth-zero roots).discontiguouslySelectedBlocksaccepts exact top-level block ranges only.The atomic mixed-content policy, descendant ownership, depth-zero outdent no-op, blank-line parser boundary, routing Tab ahead of the multi-selection beep, and one-undo intent are sound. Ensure
insertTab/insertBacktab, delegatedoCommandBy, and physical keyDown all call the same tri-state result (not handled,consumed no-op/rejected,mutated) so single non-list Tab still reaches AppKit while invalid discontiguous commands cannot mutate only the primary range.Revised implementation plan after reviewer findings —
/root/block_drag_finishNo issue #17 production code has been written yet. The earlier plan is superseded as follows:
blockSelectionAnchorSourceLocation. Restoration will map every endpoint through a prefix-delta edit index, coalesce only identical/overlapping projected ranges under a defined primary-owner rule, and restore/map the anchor when its block survives.BlockEditorView: dense/many edits retain one canonical envelope replacement; a small number of widely separated edits apply in descending order under one editor-owned mutation boundary, register one old-source Undo snapshot, and emit one commit/timing callback after the complete batch. The expected final source is built first for validation/rollback, and untouched separator bytes remain byte-identical.Reviewer re-check of the hybrid transaction and native primary ordering is active. No local build or GUI run.
Revised plan review at base
56e61d2e: one P2 blocker remains; do not start the hybrid sparse mutation path as written. The tri-state routing, selection records/affinities, ordered lookup and prefix-delta mapping, reachable-vs-injected test phases, atomic mixed rejection, and 10K functional adversarial case address the prior review.MarkdownBlockDocument.replacing(range:with:)constructs a new full source string (MarkdownBlockDocument.swift:590-597) andsetSourcesynchronizes/rebuilds after each call (:867-944). Applying even “a small number” of distant edits one-by-one therefore publishes intermediate projections and pays repeated full-source copy/synchronization costs.BlockEditorView.applyingDisplayEditsuppresses editor delegate handling; it does not turn multiple document replacements into one atomic document/projection notification. A precomputed final source and best-effort rollback cannot make observers unsee intermediate states, and rollback adds another mutation if a later step fails.Keep this feature on one existing canonical transaction: validate all edits first, build the final replacement for the smallest enclosing envelope, and call
applyCanonicalSourceReplacementonce. For two remote roots that envelope may approach the document, but it is still one copy/synchronization/Undo/commit boundary and is asymptotically safer than N calls to the currentreplacing. If profiling later proves the envelope path unacceptable, a separate document-owned batch API would need to coalesce storage edits, synchronization, projection notification, and Undo atomically; that is outside this bounded fix. Add an assertion/callback count proving exactly one source revision/commit and one Undo for remote ranges.Primary-first restoration is acceptable only if the implementation explicitly reorders records so the range containing the original primary is index 0 before
setSelectedRanges(_:affinity:stillSelecting:); preserve original order for the remainder and map the block anchor separately as proposed. Do not assume AppKit retains a primary designation attached to a non-first range.Plan gate resolved —
/root/block_drag_finishReviewer accepted the revised selection identity, linear outline construction, indexed endpoint mapping, tri-state routing, and reachable-versus-injected coverage. The proposed sparse multi-call mutation was rejected because
MarkdownBlockDocument.replacingpublishes and rebuilds on each call;applyingDisplayEditis not a document batch boundary.The candidate therefore uses exactly one validated smallest-envelope
applyCanonicalSourceReplacementcall for every Tab/Shift-Tab command. Tests will assert one source revision, one edit callback, and one Undo transaction, including remotely separated selected list blocks. Restored ranges put the original-primary owner at index 0 and preserve remaining native order.Implementation is now active in
.worktrees/editor-list-refinementsat base56e61d2e; no local build or GUI run.Pre-freeze dirty source review in
.worktrees/editor-list-refinementsat base56e61d2e: one P2 fix required before commit. No build/tests/GUI were run.Sources/CalternalEditor/BlockEditorView.swift:3658-3665: a single contiguous selection spanning list content and prose falls through to AppKit instead of being rejected atomically.listStructuralSelection()returns nil for both “no list content” and “mixed/invalid list selection.”handleListIndentthen returns.notHandledsolely becausehasMultipleBlockSelectionsis false.insertTab/insertBacktabconsequently callsuper, which can insert/traverse using that mixed selection. The intended pass-through applies only to a single wholly non-list selection; any selection touching list content plus unsupported blocks must be consumed without mutation. Fix by returning/classifyingnoList,validList, andinvalidMixed(or inspecting touched blocks before normalization), and add single contiguous list + blank/prose Tab and Shift-Tab unchanged-source cases. The existing mixed test uses two native ranges, so it cannot catch this route.The remaining inspected implementation is suitable to freeze after that fix: tri-state delegate/responder/keyDown routing is shared; the indentation stack computes parents/subtrees linearly; touched blocks are parsed once; roots/descendants are deduplicated; endpoint mapping uses ordered prefix deltas; restoration maps the block anchor and places the AppKit
selectedRange()owner first; edits use one validated canonical envelope transaction; focused tests cover delegate/responder/physical routes, CRLF, IDs, empty/task/ordered lines, reachable whole blocks, injected nested selections, one revision/commit/Undo, and a 10K functional path.AppKit guarantees
selectedRangesreadback is sorted, so the anchor-follow-up assertion is the meaningful proof of user click origin; do not describe a non-leading range as an AppKit primary selection.git diff --checkis clean.Candidate frozen for exact source review —
/root/block_drag_finish.worktrees/editor-list-refinementswave16/editor-list-refinements56e61d2e4d8da6915fc693a99b2d09d8b516086357a2fe0457cfdf2554751a3053d818daa5ae8608BlockEditorView.swiftplus focusedMarkdownBlockDocumentTests.swiftcoverage onlyImplemented:
Coverage adds reachable reversed-order Command-click whole-block selection, delegate Tab, physical Shift-Tab, follow-up Shift anchor, injected nested child/continuation and empty marker, ordered/task markers, hidden IDs, CRLF, exact blank separators, single contiguous and disjoint mixed-prose rejection, and a 10K-list many-selection functional adversary.
The final dirty-review finding is fixed: a single contiguous selection spanning list plus prose is consumed for both Tab and Shift-Tab without source/revision change. Worktree and
git diff --checkare clean. No local build or GUI run. Exact reviewer gate is active.Exact source gate —
57a2fe0457cfdf2554751a3053d818daa5ae8608(parent56e61d2e4d8da6915fc693a99b2d09d8b5160863): PASS, no P1-P3 findings. Worktree clean; no local build/tests/GUI were run.Verified:
BlockEditorView.swift:3658-3669, 3922-3932: when normalization fails,selectionTouchesListOutlineBlock()distinguishes a contiguous list+unsupported selection from ordinary prose. Both Tab and Backtab are consumed without source/revision change, and the focused test now covers that exact single-range route before the discontiguous mixed case.applyCanonicalSourceReplacement. Prefix deltas map selection endpoints in logarithmic lookup time, then source ranges are projected, overlap-coalesced, restored through nativesetSelectedRanges, and the block-selection anchor is mapped separately.setSelectedRanges(_:affinity:stillSelecting:)accepts[NSValue]; readback is sorted/nonoverlapping, which matches the assertions.git diff 57a2fe04^..57a2fe04 --checkpasses.Required next gate: compile and run the two focused tests on
agents@mbp; the 10K case is functional evidence only, so Release-app scroll/edit profiling remains required before any performance claim or issue closure.Combined integration candidate — worker
/root/block_drag_finishReviewed discontiguous Tab/Shift-Tab commit
57a2fe0457cfdf2554751a3053d818daa5ae8608was integrated conflict-free asf99ed4f28ae3b21beb87e4a99c4bf4a05acaaae8onto base26eb1552; stable patch ID is identical (d7c8afe3…). The combined worktree also contains reviewed fenced-code projection and caret-reveal deltas, so exact interaction review is active before handoff.Worktree
.worktrees/editor-structural-combinedis clean andgit diff --checkpasses. No local build or GUI run. Focused selectors:testListTabIsScopedToListsAndFirstItemCanIndent,testNativeListIndentAndNestedMovePreserveSubtreesSelectionsAndHistory,testDiscontiguousListIndentRestoresNativeSelectionsAndUsesOneTransaction, andtestManyDiscontiguousNestedListSelectionsUseOneCanonicalIndentTransaction.Source integration review — exact combined tip
5f8dd69e029e63d5c87e68d4b5e663b63684ef52against base26eb155253241603ec8f1a89e263a7a8cd496341: PASS (no P1–P3 findings).The #17 commit has an identical stable patch to the reviewed source. Multi-range list edits still form one validated canonical envelope transaction, map source endpoints through sorted edits, restore the primary native range first, preserve/map the block anchor, and consume invalid mixed selections without mutating. Its explicit post-restore reveal supersedes the transaction's stale single-selection callback through the existing selection fence. Added code-fragment and hover layout work does not alter list normalization or source mapping.
This is a source-only integration verdict. Compile, 10K functional execution, Undo, and mounted gesture verification remain with the build/QA lanes.
Root fast-forwarded the clean preview integration branch from26eb1552 to exact5f8dd69e after independent combined source review PASS and diff-check. Includes code-block presentation, atomic disjoint list indentation, native-paste reveal coalescing, and hover lifecycle/footer-width correction. The known roughly7pt visible text/mentions alignment gap remains for native measurement. This is a NEXT build candidate; it does not supersede the current26eb event/title build and is not installed. No runtime/performance acceptance is claimed.
Exact central
5f8dd69e029e63d5c87e68d4b5e663b63684ef52build-host status:SwiftPM Release test compilation stopped before any list selector could execute because
Sources/CalternalEditor/BlockEditorView.swift:7444:58calls unavailableNSColor.resolvedColor(with:); lines 7445-7446 have the same API shape. Compile log SHA-256:eff73d6e1cdd24b73777ba946f70e7ddd0af6aa15f0ac7f729b15441cf9028e2at/Users/agents/Developer/BuildOnly/package-tests-d40/Evidence/package-tests/20260913T154953Z-central-5f8dd69e-code-list-hover-1d82888f/swift-build.log.No list test executed, no runtime was staged, and no Release build was attempted. List source remains reviewer-passed but build/runtime-unverified at this exact tip.
Root took over the existing sole compiler controller after /root/build_pipeline_recover hit usage limits. Exact704b347e26c7c48f3652a4cf7cd235f7815b7364 SwiftPM Release compile PASSED and VM test runtime staged. Tests executed:0 so far. Build log SHA6d41a977d4e100e63c9f20ecfb6c500b5545d25cf17d83bb48ae25b13d6d67a9; runtime manifest8ad650319b90b9e89070f07fabace4d39b9bc276127c2f47615d5c73f70b2fb7. /root/image_preview_qa assigned exact runtime tests. Separately source-reviewed combined chrome/image-controls/cross-event candidate68885 merged cleanly with painter repair as9a69a955; that newer revision is not built. Each thread owns updates in its relevant issues. Desktop remains26eb1552.
704b VM package runtime result for discontiguous list behavior: 1/2 passed, 1 failed.
testManyDiscontiguousNestedListSelectionsUseOneCanonicalIndentTransactionpassed.testDiscontiguousListIndentRestoresNativeSelectionsAndUsesOneTransactionfailed: restored ranges were[{10,5}, {46,1}, {50,7}, {62,4}], expected[{10,5}, {44,1}, {48,7}, {60,4}]— a +2 offset in each later selection. Executed product assertion; no runner issue. Full logs VM/private/tmp/qa-704b-package-run-20260913-093737/list/; local14-package-console.txt,15-failure-extracts.txt.Current orchestration checkpoint: /root/image_preview_qa executed704b runtime9selectors:7passed,2failed. #9 mounted code gutter/resize assertions and #17 discontiguous selection restoration are assigned to /root/code_block_polish and /root/block_drag_finish respectively; no weakened assertions accepted. #4 hover and #13 native paste/Undo passed that bounded runtime. Root integrated reviewed #4 outward editor frame + explicit unmeasured-geometry docs as4ebc6804/6b7d8f83; centered760pt cap/footer width preserved, actualalignmentpending. #12 compiler repair73964bc6 is under /root/image_controls_review exact review. Central6b7d8f83 is not built; desktop26eb1552 unchanged. Each individual issue contains owning task and evidence.
Task identity:
/root/block_drag_finishRuntime failure follow-up:
/Users/kayg/Developer/calternal-app/.worktrees/editor-list-refinementswave16/editor-list-refinements57a2fe0457cfdf2554751a3053d818daa5ae8608CalternalEditorTests.MarkdownBlockDocumentTests/testDiscontiguousListIndentRestoresNativeSelectionsAndUsesOneTransaction.agents/native-break/704b-code-list-hover-qa/15-failure-extracts.txtRoot cause: the source edit adds two spaces to the selected wrapped continuation line. Marker indentation is hidden by projection, but continuation indentation is authored visible text, so later display selections correctly move by +2 UTF-16 units. The test compared post-edit absolute display offsets with the pre-edit offsets and therefore rejected correct content-following restoration.
Planned bounded repair: test-only, derive exact expected post-edit ranges from the new projection's same selected contents/empty anchor; continue asserting all four selections, primary owner, exact Markdown, one revision/commit/Undo transaction, and round-trip source. No assertion tolerance or product behavior change. No local build/GUI.
Task identity:
/root/block_drag_finishTest-only candidate:
eeed031a28cbae672de108d70738c6c89f19584eParent:
57a2fe0457cfdf2554751a3053d818daa5ae8608Worktree/branch:
.worktrees/editor-list-refinements/wave16/editor-list-refinementsThe failing assertion now checks the actual contract: every restored selection must equal the exact current projected range of its original selected contents, including the empty-list anchor, and selected substrings must remain byte-for-byte equal. It also asserts the original primary owner. Exact Markdown, one revision, one commit, one Undo transaction, and full Undo source restoration remain unchanged.
This accounts for the required +2 display shift caused only by newly visible indentation on the wrapped continuation; it does not loosen ranges or introduce tolerance.
git diff --checkpasses and the worktree is clean. No local build/GUI. Exact source review is pending, followed by the same hosted selector rerun.Exact test-only source review —
eeed031a28cbae672de108d70738c6c89f19584e(parent57a2fe0457cfdf2554751a3053d818daa5ae8608): PASS (no P1–P3 findings).The revised expectation remains exact. It captures the four distinct displayed selections before indentation (
child, the sole empty-list zero-width anchor,ordered, andtask), resolves each unique content in the post-edit projection, asserts every resolution exists, compares the complete orderedselectedRangesarray to those exact ranges, compares each selected substring to its original content, and verifies the first restored range remains primary. This catches lost, reordered, broadened, collapsed, or wrongly targeted selections while allowing the legitimate +2 display displacement caused by newly visible continuation indentation.The canonical source expectation, one source-revision increment, one commit callback, Undo availability, exact source restoration, mixed-content rejection, and commit-count checks are unchanged. Only
Tests/CalternalEditorTests/MarkdownBlockDocumentTests.swiftchanged; the API usage is compile-plausible andgit diff --checkpasses.No product behavior or assertion contract was weakened. Hosted rerun remains the execution gate.
Root exact80efddbc compile passed the prior three image-controls errors, then failed at EditorImagePresentation.swift:653: task-isolated provider self captured by MainActor.assumeIsolated loadView. Tests executed0. Log .agents/wave16/central-80efddbc-root/swift-build.log SHA07a8c920bc0deeb45d4c71c6f8318b057ee97e09c8597f6ff44dd06ad37f260d. /root/image_preview_native_repair owns SDK isolation diagnosis and review-gated repair. Root integrated reviewed #17 test correctioneeed031a asada08756: exact selection contents move with visible continuation indentation, source/Undo/revision assertions preserved. No newer desktop installed.
Exact 3bbb package compile and VM staging
Worker/role:
/root/title_completion, transfer-only infrastructure handoffWorkspace/branch:
/Users/kayg/Developer/calternal-app,dev(read-only; no source mutation)Exact central revision:
3bbb28a6fcf7bac1dae310bd07ea8e7a0ace6b68State: package test compilation and staging passed; runtime assertions pending QA
The sole
agents@mbpcontroller completedswift build -c release --build-tests --enable-xctest --disable-swift-testing -Xswiftc -enable-testingwith return code 0 in 13.916 seconds. Staging returned 0. The result recordscompileOnly=true,swiftTestInvoked=false,xctestInvoked=false, andtestsExecuted=false, so this is compile/staging evidence only.Builder evidence: build log SHA-256
54c3fe1d5cf2a36e5b3b2ead2fe915657a6c446754b5c6ac0983e48d2a81b8f1; runtime manifest167cd835ece65a3e13fec0ed235f930c0e052406ab7a05235fbc4dee8324c976; package test binary307239fd7f2fb84c172e86c4c60f529d037f9578664e3ea153f295e9c9d3a426; resultebe531e1521f3a6113fc8f6d6afec4f474587d69ebbd2dcc6d64c08e70e31ba5; source archivecca8fffc2c461a0b26703efcbea07561816ca85e10c0f770071c2d87012ee343; source manifest6f49233d1fc8510db16078a172568a4c2cb858d2041918110ba59eb4a1ce1ddf.The staged closure was archived on the builder and transferred by authenticated rsync delta into a unique VM
.partial. After its full SHA matched, it was atomically published read-only at/Users/lume/CalternalQA/3bbb28a6-package-runtime/vm-xctest-runtime-3bbb28a6-4d217fcf.tar. Builder and VM SHA-256 are both5fd5149b201d1064aa0fe9b0c3be1c96dfd83c930c380a0d712b5d2963f02e6d; mode is 0444 and the same-name partial is absent. The successful retry matched 85,579,024 of 85,634,560 bytes and sent 92,696 bytes.QA
/root/image_preview_qahas the exact path and hashes and owns unpack verification and the focused runtime selectors after the current f907 title/event lane. This task invoked no compiler, test runner, app, or GUI. Linked issues remain open pending their actual selector and user-facing acceptance evidence.3bbb VM package runtime exact hashes verified. Both discontiguous list cases passed with one executed test each:
testDiscontiguousListIndentRestoresNativeSelectionsAndUsesOneTransactionandtestManyDiscontiguousNestedListSelectionsUseOneCanonicalIndentTransaction. No timeouts. Evidence:.agents/native-break/3bbb-combined-package-qa/04-vm-summary.txtandexact-logs/list/.Root installed and launched exact3bbb28a6fcf7bac1dae310bd07ea8e7a0ace6b68 as the only ~/Applications/Calternal.app. ExecutableSHA56e57a8fc553ce7ce82f6b72dbf04f2e2d442aefb53402b943adca14c208f408; zipSHAa8b1175bc354b455426351b0b7bfc9efb826071ddf0b4b69779cac0add9adb95; strict signature/arm64/Release/revision verified. Prior26eb quit via native menu; rollback retained. Space content inventory matched before/after replacement. Native current app restored the previously open note and Style inspector without editing its content. Receipt .agents/desktop-install/3bbb28a6/install-receipt.json.
User QA preview only. Pass: basic native launch/selection; package lists2/hover1/cross-event3; separate f907event7/7. Fail/open: actual mounted code gutter7assertions, imageFit/Fill4, imageQuickLookgesture8, hostedtitle4allocatoraborts. StyledVMdeeplink did not reach note; nativeUIfallback assigned. Each failure has independent author+reviewer, not closed. No stable or performance acceptance; latest imageprovider repair and diagnostics are NOT in this desktoppreview.
User test focus: centered width and mentions alignment, hover-only six dots, block selection/dragging, cross-event Copy/Move/Cancel/Remember dialog. These UI outcomes remain user acceptance gates.
Remote preservation checkpoint — 2026-09-14
The following original-worktree candidates are now pushed and exact hashes verified on both Forgejo and GitHub. These are preserved candidates/diagnostics, not a claim of new acceptance; some may already be integrated by cherry-pick or superseded. Use the issue’s existing evidence before choosing changes to resume.
codex/wip/editor-hr-leaf-fix-20260914—6450f21ea42f84a71f0917f2edc2d58aac0b3899wave14/editor-block-drag—05fefc98890003a4d14ff297d4ead459deca0538wave14/editor-empty-marker-host-repro—79dc861166b57a70a29e6591c1dc1effbd4f7a82wave14/editor-empty-marker-map-publication—c2dcd6d05e22d0ef39b9da8d9e37fa95ec03e2bewave14/editor-empty-marker-paint—e32565510ac13063a5c72994ce0db16dae7ac355wave14/editor-empty-marker-preview—10d6af95f1fe4d786c6c7423d4154bc9e39a2bcewave14/editor-empty-marker-probe—bded2b644854c65f4e1c4dfbaeedf30302b19f34wave14/editor-empty-marker-runtime-diag—bf946ddd7a0c9b0e89e73dc6cc85f1461228b9e4wave14/editor-list-enter-next—66c4af4aaddb3b09f7366211d48d4533d8b08cf6wave14/editor-list-handle-integrated—3441a34263f24aab2c4a8142db103ca1cd8ee3e6wave14/editor-list-keyboard-audit—846a9c768425e2bec809063aae09c3dd6f16ba78wave16/editor-list-refinements—eeed031a28cbae672de108d70738c6c89f19584eMaster #20 contains the resume instructions. No force push; dev remains
1d8f4adf.Rendering defects observed on the VM while verifying hover (build from
wave19/block-editor-craftafter45a700b, but the same rendering is visible on dev1d8f4adfin the earlier baseline capture): in the fixture noteA0 Block Editor and Style QA(Overnight Perf QA / Native Break spaces) the ordered-list markers1.are painted one line BELOW their item text ("populated ordered item" text at y≈557, its1.at y≈578; the empty second item's1.at y≈626 with no text beside it), and consecutive ordered items both show1.instead of 1./2. Evidence:.agents/evidence/block-editor/walk-grid.png,hov-1.png.