shadow: refuse daemon-impossible grid values instead of normalizing them #318

Merged
natsukium merged 10 commits from shadow-admission into main 2026-09-21 18:35:32 +09:00
Owner

One policy at the wire-to-shadow boundary: a GridMsg value the daemon could not have produced is ShadowError, and
the client closes only that attachment. Reconnect and rehydrate recover it; the daemon session and the other
subscribers are untouched. No clamping, no silent drop, no degraded rendering.

The admission rules

Value Admitted
RowDelta row index below MAX_GRID_ROWS (the protocol-wide bound; MAX_SHADOW_ROWS is gone)
RowDelta body width (the row codec's cols) at most MAX_GRID_COLS, checked before decode_row reserves its cells
a row's sized-cell column inside that row's decoded columns
a row's Cluster / Hyperlink handle already installed
Scrolled.region_top / region_bottom region_top <= region_bottom, both inside the announced grid
Scrolled.n_rows at least 1 and at most the region's height
CursorState.row / col inside the announced grid, visible or not
ViewportState.lines_from_bottom at most max
Cluster.id / Hyperlink.id nonzero and within the table caps
Cluster.text, Hyperlink.anchor / uri within ClusterText::CAP / LinkText::CAP

Two judgement calls the issues left open:

  • The announced grid, not the shadow's own dimensions, is what a coordinate is measured against. The daemon states
    its geometry before composing anything at a new one, while a client resizes its shadow optimistically ahead of the
    daemon honoring the resize; measuring against the shadow would kill an attachment on every window-resize drag.
  • An invisible cursor carries the same coordinate requirement as a visible one: the daemon reports where its cursor
    stands either way, and ModeFlags can reveal it without a new coordinate.

The resize race keeps its tolerance, which is why the row index and the body width are the only grid values bounded
protocol-wide: a row lower or wider than the mirror grows it, a narrower one pads it.

MAX_CELLS_PER_ROW narrows from the u16 field width to MAX_GRID_COLS, so the over-wide row is refused in
decode_row before the Vec<Cell> reserve rather than truncated after it; MAX_ATTR_RUNS and MAX_SIZED_CELLS
follow. ScreenBuffer::apply_scroll_directive and set_cursor_state no longer clamp, and install_cluster /
install_hyperlink report a refusal instead of swallowing it.

Tests

felis-client-core shadow tests: the_last_admitted_row_is_accepted_and_one_past_it_is_refused,
a_row_wider_than_the_admitted_grid_is_refused_before_it_allocates, a_sized_cell_outside_the_decoded_row_is_refused,
scroll_regions_and_counts_outside_the_grid_are_refused,
cursor_coordinates_outside_the_grid_are_refused_visible_or_not, a_resize_race_still_applies_in_either_direction,
a_viewport_past_its_own_maximum_is_refused, a_zero_registry_id_is_refused,
an_over_length_registry_entry_is_refused, a_cluster_id_at_the_table_cap_installs_and_one_past_it_is_refused,
a_flood_of_hyperlinks_from_a_peer_fails_at_the_table_budget,
a_row_naming_an_unarrived_registry_entry_ends_the_attachment,
a_row_applies_when_its_entries_arrived_first_despite_unrelated_holes.

felis-grid: a_sized_cell_past_the_decoded_row_is_rejected, and the scroll-directive property now asserts that a
directive outside the grid is refused rather than normalized.

felis-daemon: an_attachment_that_closes_leaves_the_session_and_its_peers_running pins that one refusal costs the
attachment alone.

proptest_cluster_convergence states the policy it used to contradict: a permuted entry order still converges, and a
row before its entries is refused. proptest_row_delta_batch and rehydration_snapshot pass, the former now shipping
the registry entries its rows name first, as the daemon does.

Docs

docs/reference/ipc.md gains a "Grid admission" section under Corruption with one rule per value, and its semantic
limits, registry delivery, and GridMsg entries agree with it. The row-codec reference carries the narrowed counts and
the side-band column rule. docs/explanation/architecture/ipc.md records why normalizing was rejected;
docs/explanation/data-model/grid-and-cells.md no longer promises a degraded cell. docs/reference/testing.md and
CHANGELOG.md follow.

just check gates run: cargo nextest run --workspace --all-features (3343 passed), cargo clippy --workspace --all-targets (0 warnings), just prose-check, just docs-links, treefmt.

Closes #304
Closes #308
Closes #315

One policy at the wire-to-shadow boundary: a `GridMsg` value the daemon could not have produced is `ShadowError`, and the client closes only that attachment. Reconnect and rehydrate recover it; the daemon session and the other subscribers are untouched. No clamping, no silent drop, no degraded rendering. ## The admission rules | Value | Admitted | | ---------------------------------------------- | ---------------------------------------------------------------------- | | `RowDelta` row index | below `MAX_GRID_ROWS` (the protocol-wide bound; `MAX_SHADOW_ROWS` is gone) | | `RowDelta` body width (the row codec's `cols`) | at most `MAX_GRID_COLS`, checked before `decode_row` reserves its cells | | a row's sized-cell column | inside that row's decoded columns | | a row's `Cluster` / `Hyperlink` handle | already installed | | `Scrolled.region_top` / `region_bottom` | `region_top <= region_bottom`, both inside the announced grid | | `Scrolled.n_rows` | at least 1 and at most the region's height | | `CursorState.row` / `col` | inside the announced grid, visible or not | | `ViewportState.lines_from_bottom` | at most `max` | | `Cluster.id` / `Hyperlink.id` | nonzero and within the table caps | | `Cluster.text`, `Hyperlink.anchor` / `uri` | within `ClusterText::CAP` / `LinkText::CAP` | Two judgement calls the issues left open: - **The announced grid, not the shadow's own dimensions**, is what a coordinate is measured against. The daemon states its geometry before composing anything at a new one, while a client resizes its shadow optimistically ahead of the daemon honoring the resize; measuring against the shadow would kill an attachment on every window-resize drag. - **An invisible cursor carries the same coordinate requirement as a visible one**: the daemon reports where its cursor stands either way, and `ModeFlags` can reveal it without a new coordinate. The resize race keeps its tolerance, which is why the row index and the body width are the only grid values bounded protocol-wide: a row lower or wider than the mirror grows it, a narrower one pads it. `MAX_CELLS_PER_ROW` narrows from the `u16` field width to `MAX_GRID_COLS`, so the over-wide row is refused in `decode_row` before the `Vec<Cell>` reserve rather than truncated after it; `MAX_ATTR_RUNS` and `MAX_SIZED_CELLS` follow. `ScreenBuffer::apply_scroll_directive` and `set_cursor_state` no longer clamp, and `install_cluster` / `install_hyperlink` report a refusal instead of swallowing it. ## Tests `felis-client-core` shadow tests: `the_last_admitted_row_is_accepted_and_one_past_it_is_refused`, `a_row_wider_than_the_admitted_grid_is_refused_before_it_allocates`, `a_sized_cell_outside_the_decoded_row_is_refused`, `scroll_regions_and_counts_outside_the_grid_are_refused`, `cursor_coordinates_outside_the_grid_are_refused_visible_or_not`, `a_resize_race_still_applies_in_either_direction`, `a_viewport_past_its_own_maximum_is_refused`, `a_zero_registry_id_is_refused`, `an_over_length_registry_entry_is_refused`, `a_cluster_id_at_the_table_cap_installs_and_one_past_it_is_refused`, `a_flood_of_hyperlinks_from_a_peer_fails_at_the_table_budget`, `a_row_naming_an_unarrived_registry_entry_ends_the_attachment`, `a_row_applies_when_its_entries_arrived_first_despite_unrelated_holes`. `felis-grid`: `a_sized_cell_past_the_decoded_row_is_rejected`, and the scroll-directive property now asserts that a directive outside the grid is refused rather than normalized. `felis-daemon`: `an_attachment_that_closes_leaves_the_session_and_its_peers_running` pins that one refusal costs the attachment alone. `proptest_cluster_convergence` states the policy it used to contradict: a permuted entry order still converges, and a row before its entries is refused. `proptest_row_delta_batch` and `rehydration_snapshot` pass, the former now shipping the registry entries its rows name first, as the daemon does. ## Docs `docs/reference/ipc.md` gains a "Grid admission" section under Corruption with one rule per value, and its semantic limits, registry delivery, and `GridMsg` entries agree with it. The row-codec reference carries the narrowed counts and the side-band column rule. `docs/explanation/architecture/ipc.md` records why normalizing was rejected; `docs/explanation/data-model/grid-and-cells.md` no longer promises a degraded cell. `docs/reference/testing.md` and `CHANGELOG.md` follow. `just check` gates run: `cargo nextest run --workspace --all-features` (3343 passed), `cargo clippy --workspace --all-targets` (0 warnings), `just prose-check`, `just docs-links`, `treefmt`. Closes #304 Closes #308 Closes #315
The shadow used to clamp, drop, or degrade every grid value it could not
place: a row index past the admitted bound vanished, an over-wide row was
truncated, scroll regions and cursor coordinates were clamped into the
grid, and a row naming a registry entry that had not arrived drew a
fallback cell. Each of those leaves the client painting a screen the
daemon never composed while it still reports a healthy connection, and
none of them has a later event that repairs the divergence.

Under the connection-local corruption policy the honest answer is the one
a corrupt frame already gets: refuse the value before touching a cell and
close that attachment. Reconnect and rehydrate rebuild the mirror, and
the daemon session and the other subscribers are untouched, so the cost
is one blink instead of a silently wrong terminal.

The one accommodation kept is the resize race, where a row lower or wider
than the mirror has an honest reading; its bound is therefore the
protocol-wide geometry rather than the announced grid every other
coordinate is measured against. The row codec's counts narrow from the
u16 field width to MAX_GRID_COLS for the same reason: no grid can
produce a wider row, and the count drives a reservation.

Assisted-by: Claude Code
shadow: state the admission comments in the house prose form
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Has been cancelled
darwin / detect relevant changes (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
darwin / darwin docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
f2f3d5c848
The em-dash asides and the over-long blocks these comments carried are
what `just prose-check` refuses: an aside long enough to need a dash is
an argument, and an argument belongs in the owning doc or the commit
body rather than at the call site.

Assisted-by: Claude Code
grid: hold the authoritative cursor and retire stale scroll directives
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Has been cancelled
darwin / detect relevant changes (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
darwin / darwin docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
5180206a05
Refusing daemon-impossible grid values exposed two ways the daemon and
the shadow could produce them honestly.

A queued `GridMsg::Scrolled` describes the geometry it was recorded
under, and nothing retired it: a pull-paced subscriber could receive
`Size { rows: 10 }` followed by a directive naming row 23, which the
admission rule refuses and an honest attachment pays for with a
reconnect. An in-bounds stale directive is worse, shifting rows the
reflow already moved with nothing to report. Every `PtyEffect::Scrolled`
now carries the grid generation it was produced under, which the session
task compares against the grid it is about to compose from; a stale one
downgrades to a row replay. Stamping at production rather than at drain
is what covers the path where a resize runs before the effects it
preceded are drained. The resize itself clears what each subscriber has
queued, the reflow having marked every row dirty in their place.

The cursor the daemon states outlives the shadow's own geometry for the
same reason. A client shrinks its shadow optimistically, which clamps
the grid's cursor; when another subscriber owns the PTY size the daemon
answers with the authoritative `Size` and a full row replay but no
`CursorState`, since its cursor never moved, so the clamped position
would stand for good. The shadow keeps the stated cursor beside the
grid's and re-places it after every geometry change, which also lets a
coordinate that arrives while the local shadow is too small keep its
visibility, style, and blink instead of being dropped.

Assisted-by: Claude Code
grid: answer every mirror on a resize and install a registry id once
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Has been cancelled
darwin / detect relevant changes (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
darwin / darwin docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
3ef0bc79a1
Two more ways an honest peer could produce what the shadow now refuses,
or diverge without it noticing.

A resize composed only for the subscriber that asked for it. An idle
mirror has its next frame already requested, and the broadcast `Size`
does not answer a pull, so nothing replaced the rows the resize had just
invalidated until the program next wrote something: the mirror kept
painting content composed at dimensions the daemon had overruled. The
resize now composes for every eligible subscriber, after the
announcement so the replay follows the dimensions it belongs to.

Both registry tables took a second entry under an occupied id and
reported success, which redefines what cells holding that handle already
drew while the connection reads healthy. An id is now installed once:
the tables refuse an occupied slot and the shadow ends the attachment,
naming the id. An identical re-send is refused by the same rule rather
than carved out, because the daemon sends no entry twice to a connection
either way, and `RehydrateBegin` empties the table before an attach
burst, so nothing legitimate reinstalls. The daemon's own grid mints
handles through `intern`, which is untouched.

Assisted-by: Claude Code
daemon: let a resize consume the damage it produced
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Has been cancelled
darwin / detect relevant changes (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
darwin / darwin docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
3c5e433c62
The resize marked every subscriber dirty and shipped that replay, but
left the grid's own reflow damage armed. The caller's fan-out then
merged it into every subscriber a second time: an eager requester
received two full replays back to back, and a pull-paced mirror one
behind the `Size` and another on its next otherwise-clean pull, which is
both a broken clean-pull contract and a way to push a slow subscriber
past its buffer cap.

The resize now fans the grid state out itself, ahead of the announcement
it composes for, so the caller's fan-out finds nothing left to re-arm.
Draining there also retires the scroll effects the resize invalidated on
the same pass.

Assisted-by: Claude Code
daemon: drop a scroll directive the replay already carries
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 5s
darwin / darwin docs-only no-op (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Failing after 2m19s
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
c182441ec4
A resize fans its damage out before the replay is encoded, so the parse
thread can scroll the grid in between. The replay then holds the shifted
rows, while the queued directive still names the current geometry and
would shift them a second time when it is drained.

Stamp every scroll effect with the grid's scroll sequence and record,
per subscriber, the sequence its last full replay was composed at. A
directive at or below that sequence is already in the rows the
subscriber holds, so it is dropped; a partial replay leaves the sequence
alone, because there the shift still has to be shipped.

Assisted-by: Claude Code
daemon: compose rows and scroll directives from one grid reading
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Failing after 2m20s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
037e5ef6a9
The core lock is taken per subscriber, so the parse thread can scroll
between two of them. The row cache then handed the second mirror rows
from the older grid while its accounted sequence advanced past the
scroll, and the directive was dropped as redundant: that mirror stayed
one shift behind. A scroll landing after the fan-out was worse, and this
one predates the sequence accounting: the partial replay read
post-scroll cells, nothing accounted for the shift, and the directive
drained next shifted those cells a second time.

Key the row cache by the grid it encoded from, and give each subscriber
the sequence a fan-out last offered it. A composition that finds the
grid past that sequence is reading cells no directive it holds accounts
for, so it replays the rows at that reading instead.

Assisted-by: Claude Code
docs: state what the row cache actually guarantees
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Has been cancelled
windows / cargo clippy (Windows cross) (pull_request) Has been cancelled
windows / cargo nextest (Windows) (pull_request) Has been cancelled
windows / frontend smoke (Windows) (pull_request) Has been cancelled
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been cancelled
windows / windows docs-only no-op (pull_request) Has been cancelled
bench / Criterion regression gate (pull_request) Has been cancelled
darwin / detect relevant changes (pull_request) Has been cancelled
darwin / build felis (aarch64-darwin) (pull_request) Has been cancelled
darwin / darwin docs-only no-op (pull_request) Has been cancelled
fuzz / cargo fuzz smoke (per target) (pull_request) Has been cancelled
pr / nix flake check (pull_request) Has been cancelled
pr / cargo build / clippy / test / deny (pull_request) Has been cancelled
pr / wire schema is compatible with the base (pull_request) Has been cancelled
pr / frontend smoke (x86_64-linux) (pull_request) Has been cancelled
pr / publish felis (x86_64-linux) (pull_request) Has been cancelled
10799bc452
The cache is keyed by geometry and scroll order, not by cell content,
and each composition takes the core lock for itself. Claiming one
instant across the ship loop, or one encode per row, promised a
consistency the key does not carry and that the scroll accounting does
not rest on.

Assisted-by: Claude Code
Merge remote-tracking branch 'origin/main' into shadow-admission
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Failing after 2m20s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 54s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m14s
pr / nix flake check (pull_request) Successful in 42s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m49s
pr / wire schema is compatible with the base (pull_request) Successful in 9s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m10s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 4s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo nextest (Windows) (pull_request) Successful in 5m54s
windows / frontend smoke (Windows) (pull_request) Successful in 2m26s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 12s
056828581a
# Conflicts:
#	CHANGELOG.md
Merge branch 'main' into shadow-admission
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Failing after 3m6s
darwin / detect relevant changes (pull_request) Successful in 24s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 51s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m17s
pr / wire schema is compatible with the base (pull_request) Successful in 9s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 2m0s
pr / nix flake check (pull_request) Successful in 48s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m14s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 5s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo nextest (Windows) (pull_request) Successful in 6m48s
windows / frontend smoke (Windows) (pull_request) Successful in 2m11s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 19s
46620dd669
Assisted-by: Claude Code
natsukium deleted branch shadow-admission 2026-09-21 18:35:32 +09:00
Commenting is not possible because the repository is archived.
No description provided.