Phase 3: Gapped integer positions for block inserts #188
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/pal-e-api#188
Loading…
Add table
Add a link
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?
Lineage
plan-2026-03-16-knowledge-architecture→ Phase 3 (Gapped Integer Positions)Repo
forgejo_admin/pal-e-docsUser Story
As a session manager using
create_blockto insert content into existing notes,I want to insert a block between two existing blocks without shifting every block after the insertion point,
so that inserts are O(1) instead of O(n) and don't touch rows I didn't intend to modify.
Context
Inserting a heading at position 20 in a note with 40 blocks currently requires the API to shift all blocks at positions 20+ up by 1 — that's 20 unnecessary writes. Similarly, deleting any block triggers
_reindex_positionswhich rewrites ALL block positions to sequential 0,1,2,3...The fix is gapped integers: blocks get positions 0, 1000, 2000, 3000... Inserting between 1000 and 2000 uses position 1500 with zero other writes. Deleting a block just leaves a gap — gaps are normal. A rebalance endpoint exists for the rare case where gaps exhaust.
This only applies to Block positions. Note child positions and board item positions are out of scope (they already tolerate gaps/collisions).
File Targets
Files to modify:
src/pal_e_docs/blocks/parser.pylines 63-64 — changeposition = len(blocks)toposition = len(blocks) * 1000(gapped spacing). Also update the bare-text paragraph path around line 53 to usepos * 1000.src/pal_e_docs/routes/blocks.pylines 49-54 (_reindex_positions) — keep the function but do NOT call it fromdelete_block. It will be used only by the new rebalance endpoint (with gapped spacing:i * 1000instead ofi).src/pal_e_docs/routes/blocks.pylines 235-243 (create_block shift logic) — change to only shift when there's an exact position collision (existing block at the exact same position), not all blocks atposition >= target. When shifting for a collision, shift just the colliding block by +1.src/pal_e_docs/routes/blocks.pyline 286 (delete_block) — remove the_reindex_positionscall. Gaps after delete are normal.src/pal_e_docs/routes/blocks.py(new endpoint) — addPOST /notes/{slug}/blocks/rebalancethat renumbers all blocks to gapped positions (0, 1000, 2000...) preserving current order.Files NOT to touch:
src/pal_e_docs/blocks/sync.py—parse_and_store_blockstakes positions from parser dict, which will already have gapped values. No changes needed.src/pal_e_docs/blocks/compiler.py— compiles from block list, position-agnostic.src/pal_e_docs/routes/notes.py— note-level endpoints unaffected.src/pal_e_docs/models.py— Block.position is already an Integer column, no schema changes.Acceptance Criteria
parse_and_store_blockscreates blocks from HTML, positions are 0, 1000, 2000, 3000... (gap of 1000)create_block(position=1500)is called and no block exists at 1500, the block is inserted with zero other position changescreate_block(position=1000)is called and a block already exists at 1000, the existing block at 1000 shifts to 1001 (backward compat)delete_blockis called, remaining blocks keep their positions — no reindexing to sequentialget_sectionstill works — section retrieval usesBlock.position >range queries, which work with gapsPOST /notes/{slug}/blocks/rebalancerenumbers all blocks to gapped positions (0, 1000, 2000...) preserving orderpytest tests/)Test Expectations
pytest tests/pytest tests/ -x -qConstraints
routes/blocks.py(FastAPI, Depends(get_db), response_model pattern)POSITION_GAP = 1000) defined once and imported where needed_reindex_positionshelper should be updated to use gapped spacing (i * POSITION_GAP) and renamed to_rebalance_positionsfor clarity, but only called from the rebalance endpointChecklist
Related
project-pal-e-docs— project this affectsplan-2026-03-16-knowledge-architecture— parent plan