Follow kitty's placement identity and lifetime rules #87

Merged
natsukium merged 3 commits from fix/idless-placements into main 2026-10-10 11:47:16 +09:00
Owner

Closes #78. Closes #79.

The alternate screen is out of scope: felis shares one image store between screens where kitty keeps one per screen, which is #89. The placement table's size bound is #88.

daemon, grid: delete an image's placements when it is transmitted again. Re-transmitting under a live image id left its placements on screen showing the new pixels, with no ref pinning the image; kitty deletes them (free_refs_data). Re-placing under the same (i, p) also took a second ref while replacing the placement, pinning the image after it was deleted. Both are stated as one property over puts, deletes and re-transmits against a model, with every live placement holding exactly one ref.

grid, daemon: give each Kitty placement without p= a placement of its own. The table kept one id-less slot per image, so placing an image twice without p= moved the first copy; kitty replaces only under a non-zero p=.

  • An id-less put (no p=, or p=0) gets an id counted down from u32::MAX, like an unnamed image's id, and is marked anonymous. The wire and client shadow key on (image, placement id) and need no change.
  • No producer p= addresses an anonymous placement: named puts and deletes skip it, and a named put landing on its id first moves it to a fresh one at the end of the table (announced as a remove and a re-place, which the client appends).
  • A delete with p=0 counts as one without p=; p= on an image sent without i=/I= is ignored; the reply echoes p= only when non-zero.
  • The property gains id-less, p=0 and u32::MAX ids; each of the four fixes above was removed by hand and the property failed every time.

daemon: free an image on an uppercase delete only once it is unplaced. d=I with p= and the d=P/Q/X/Y/Z/C/R predicates freed the image even when other placements still used it, orphaning them; kitty's filter_refs frees only once no refs remain. The property gains d=I and tracks whether the image is still stored.

Verification

  • Raw-escape repro in a felis window (headless sway screenshot): two a=p,i=1,c=10,r=5,C=1 at different cells draw two copies, as in kitty; before, one.
  • The alt-screen property passes unchanged with id-less placements. the_mirror_keeps_the_daemons_placement_order ships every step to a client shadow and checks it holds the daemon's placements in the daemon's order.
  • just check passes apart from six felis-shaping tests that fail on main too on this host (pinned font files missing).
Closes #78. Closes #79. The alternate screen is out of scope: felis shares one image store between screens where kitty keeps one per screen, which is #89. The placement table's size bound is #88. **daemon, grid: delete an image's placements when it is transmitted again.** Re-transmitting under a live image id left its placements on screen showing the new pixels, with no ref pinning the image; kitty deletes them (`free_refs_data`). Re-placing under the same `(i, p)` also took a second ref while replacing the placement, pinning the image after it was deleted. Both are stated as one property over puts, deletes and re-transmits against a model, with every live placement holding exactly one ref. **grid, daemon: give each Kitty placement without p= a placement of its own.** The table kept one id-less slot per image, so placing an image twice without `p=` moved the first copy; kitty replaces only under a non-zero `p=`. - An id-less put (no `p=`, or `p=0`) gets an id counted down from `u32::MAX`, like an unnamed image's id, and is marked anonymous. The wire and client shadow key on `(image, placement id)` and need no change. - No producer `p=` addresses an anonymous placement: named puts and deletes skip it, and a named put landing on its id first moves it to a fresh one at the end of the table (announced as a remove and a re-place, which the client appends). - A delete with `p=0` counts as one without `p=`; `p=` on an image sent without `i=`/`I=` is ignored; the reply echoes `p=` only when non-zero. - The property gains id-less, `p=0` and `u32::MAX` ids; each of the four fixes above was removed by hand and the property failed every time. **daemon: free an image on an uppercase delete only once it is unplaced.** `d=I` with `p=` and the `d=P/Q/X/Y/Z/C/R` predicates freed the image even when other placements still used it, orphaning them; kitty's `filter_refs` frees only once no refs remain. The property gains `d=I` and tracks whether the image is still stored. ## Verification - Raw-escape repro in a felis window (headless sway screenshot): two `a=p,i=1,c=10,r=5,C=1` at different cells draw two copies, as in kitty; before, one. - The alt-screen property passes unchanged with id-less placements. `the_mirror_keeps_the_daemons_placement_order` ships every step to a client shadow and checks it holds the daemon's placements in the daemon's order. - `just check` passes apart from six `felis-shaping` tests that fail on `main` too on this host (pinned font files missing).
grid, daemon: give each Kitty placement without p= a placement of its own
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Failing after 2m12s
windows / detect relevant changes (pull_request) Successful in 10s
windows / windows docs-only no-op (pull_request) Has been skipped
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m27s
pr / nix flake check (pull_request) Successful in 40s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m44s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 30s
windows / cargo clippy (Windows cross) (pull_request) Successful in 22s
windows / cargo nextest (Windows) (pull_request) Successful in 4m9s
windows / frontend smoke (Windows) (pull_request) Successful in 36s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
3eaf4ad68b
The placement table keyed an id-less put as the image's single default
slot, so placing one image twice without p= moved the first copy to the
second position. kitty replaces only under a non-zero placement id:
handle_put_command reuses a ref only when p is set and the image has a
client id, and the spec says id-less puts of one image "result in
multiple placements".

An id-less put now gets an id from the placement table, counted down
from u32::MAX and skipping the image's live ids, the way an unnamed
image gets its image id. The wire and the client shadow already key on
(image, placement id), so they need no change and tell the copies
apart. p= on an image sent without i= or I= is ignored, as in kitty, and
the reply echoes p= only when it is non-zero, which the property over
mixed id-less, p=0 and named puts turned up.

The alt-screen property now places with p=1: it re-transmits under one
image id, and with id-less puts accumulating, the store's refcount reset
on re-transmit (which leaves the old placements behind) would break its
refcount invariant. That reset is a separate bug.

Assisted-by: Claude Code
natsukium force-pushed fix/idless-placements from 3eaf4ad68b
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Failing after 2m12s
windows / detect relevant changes (pull_request) Successful in 10s
windows / windows docs-only no-op (pull_request) Has been skipped
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m27s
pr / nix flake check (pull_request) Successful in 40s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m44s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 30s
windows / cargo clippy (Windows cross) (pull_request) Successful in 22s
windows / cargo nextest (Windows) (pull_request) Successful in 4m9s
windows / frontend smoke (Windows) (pull_request) Successful in 36s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
to 70f4138d37
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 10s
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m9s
bench / Criterion regression gate (pull_request) Successful in 2m5s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m26s
windows / cargo nextest (Windows) (pull_request) Successful in 3m38s
pr / nix flake check (pull_request) Successful in 42s
windows / frontend smoke (Windows) (pull_request) Successful in 48s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m27s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
2026-10-09 23:39:31 +09:00
Compare
natsukium changed title from Give each Kitty placement without p= a placement of its own to Follow kitty's placement identity and lifetime rules 2026-10-09 23:56:07 +09:00
natsukium force-pushed fix/idless-placements from 70f4138d37
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 10s
windows / windows docs-only no-op (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m5s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m26s
windows / frontend smoke (Windows) (pull_request) Successful in 48s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m27s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m9s
windows / cargo nextest (Windows) (pull_request) Successful in 3m38s
pr / nix flake check (pull_request) Successful in 42s
to b022cb0eea
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
windows / detect relevant changes (pull_request) Successful in 10s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Successful in 2m6s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m26s
windows / cargo nextest (Windows) (pull_request) Successful in 3m42s
pr / nix flake check (pull_request) Successful in 42s
windows / frontend smoke (Windows) (pull_request) Successful in 44s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m22s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
2026-10-09 23:56:08 +09:00
Compare
natsukium force-pushed fix/idless-placements from b022cb0eea
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
windows / detect relevant changes (pull_request) Successful in 10s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Successful in 2m6s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m26s
windows / cargo nextest (Windows) (pull_request) Successful in 3m42s
pr / nix flake check (pull_request) Successful in 42s
windows / frontend smoke (Windows) (pull_request) Successful in 44s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m22s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
to 34f217e80c
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
windows / detect relevant changes (pull_request) Successful in 10s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Successful in 2m5s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m30s
windows / cargo nextest (Windows) (pull_request) Successful in 3m53s
pr / nix flake check (pull_request) Successful in 41s
windows / frontend smoke (Windows) (pull_request) Successful in 45s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m26s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
2026-10-10 04:07:38 +09:00
Compare
natsukium force-pushed fix/idless-placements from 34f217e80c
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
windows / detect relevant changes (pull_request) Successful in 10s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Successful in 2m5s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m30s
windows / cargo nextest (Windows) (pull_request) Successful in 3m53s
pr / nix flake check (pull_request) Successful in 41s
windows / frontend smoke (Windows) (pull_request) Successful in 45s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m26s
pr / wire schema is compatible with the base (pull_request) Successful in 3s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
to 13cf708741
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 8s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m8s
bench / Criterion regression gate (pull_request) Successful in 2m8s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m32s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m25s
windows / detect relevant changes (pull_request) Successful in 12s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo nextest (Windows) (pull_request) Successful in 3m51s
pr / nix flake check (pull_request) Successful in 41s
windows / frontend smoke (Windows) (pull_request) Successful in 46s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
pr / wire schema is compatible with the base (pull_request) Successful in 6s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
2026-10-10 04:19:08 +09:00
Compare
natsukium deleted branch fix/idless-placements 2026-10-10 11:47:16 +09:00
Sign in to join this conversation.
No reviewers
No milestone
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
felis-terminal/felis!87
No description provided.