Phase 3: Gapped Integer Positions

phase-2026-03-16-3-gapped-positions Phase

phase

Phase 3: Gapped Integer Positions

Goal: Eliminate cascading position shifts on block inserts by using gapped integers (spacing of 1000) with periodic rebalance.
Owner: Dev agent
Repo: forgejo_admin/pal-e-docs
Depends on: None (independent of phases 1-2)

Scope

  • Block positions: New blocks get positions spaced by 1000 (0, 1000, 2000, ...). Insert between 19000 and 20000 → position 19500. No other rows touched.
  • Rebalance endpoint: POST /notes/{slug}/blocks/rebalance renumbers all blocks to gapped positions preserving order.
  • Collision handling: Insert at occupied position shifts only the collider (+1). Gap exhaustion triggers automatic rebalance before insert.
  • Delete preserves gaps: No more sequential reindexing on delete.

Deliverables

  • PR #189 — merged 2026-03-17
  • POSITION_GAP = 1000 constant in blocks/parser.py, exported from blocks/__init__.py
  • Parser produces gapped positions (0, 1000, 2000...)
  • Collision-only shift in create_block (was: shift all blocks at position >= target)
  • Gap-preserving delete (was: reindex to sequential after every delete)
  • POST /notes/{slug}/blocks/rebalance endpoint with RebalanceOut schema
  • 24 new tests in test_gapped_positions.py, 4 existing test files updated
  • 631 total tests passing

Deferred Scope

  • Note child positions — not needed (already gap-tolerant)
  • BoardItem positions — not needed (already tolerate gaps/collisions)
  • Data migration — not needed (old sequential positions still work)

QA Nits (non-blocking)

  • Stale docstring in parser.py lines 33/38 — still references "0-based ordering" and "paragraph-3" examples
  • _seed_note_with_blocks in test_blocks_api.py could use a comment clarifying intentional use of legacy sequential positions
  • plan-2026-03-16-knowledge-architecture — parent plan
  • Forgejo issue #188 (closed)