Skip to main content

max / quasi

An arm does not adopt the loop that repeats it MNW's feed is the site and its residual test is what found it: a pager's jump is one member of the `Rest`, the loop over jumps runs one pass while staging, and the `here` setting makes an arm of that same member. So the loop's cover and the arm's cover are exactly the same bytes, `spans_within` read equal as inside, and the arm adopted the loop as a child. The residual came out with a `Loop` inside an `Arms`, which the fill program has no instruction for, and the filler stopped one op in. Range alone cannot tell the two apart. Recording order can, and it is the rule `tree` already uses for the same reason: a container adds its marks innermost first, so a cover recorded after this one encloses it. An arm now adopts only covers recorded before it. `spliced` grows the two levels it was missing, which are the two that made MNW's feed different from every fixture: a guarded `include` of a shape that loops, and a described pager inside it. The panic that pointed at this is worth keeping too -- it now says how far the walk got and what the first op left is, which is what turned "the filler stopped" into a diagnosis.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
Author: Max Johnson <me@maxj.phd> · 2026-09-08 22:45 UTC
Signed with PGP, not checked
Commit: 12efc11a07b9dfeb86d1864fab374ecd7d26b380
Parent: 06a9c02
3 files changed, +77 insertions, -16 deletions
M Cargo.lock +4 -4
@@ -6314,10 +6314,6 @@
6314 6314 "winnow 1.0.4",
6315 6315 ]
6316 6316
6317 - [[patch.unused]]
6318 - name = "synckit-client"
6319 - version = "0.10.0"
6320 -
6321 6317 [[patch.unused]]
6322 6318 name = "kberg"
6323 6319 version = "0.1.0"
@@ -6337,3 +6333,7 @@
6337 6333 [[patch.unused]]
6338 6334 name = "quasi-type"
6339 6335 version = "0.1.3"
6336 +
6337 + [[patch.unused]]
6338 + name = "synckit-client"
6339 + version = "0.10.0"
@@ -22,11 +22,54 @@
22 22 #![allow(dead_code)]
23 23
24 24 use quasi_declare::declare;
25 + use quasi_router::Action;
26 + use quasi_router::screen::{Jump, Rest};
25 27
26 28 /// What the panel read.
27 29 pub(crate) struct Feed {
28 30 pub heading: String,
29 31 pub items: Vec<String>,
32 + /// The pages the strip offers, which is the description's own window.
33 + pub pages: Vec<usize>,
34 + }
35 +
36 + impl Feed {
37 + /// Whether this is the page the reader is on.
38 + ///
39 + /// A fixed answer rather than a field, because what the strip needs is one
40 + /// jump differing from its siblings and which one is not interesting here.
41 + pub(crate) fn here(&self, page: usize) -> bool {
42 + page == self.pages.first().copied().unwrap_or_default()
43 + }
44 + }
45 +
46 + declare! {
47 + /// A table of the feed's items, in a shape of its own.
48 + ///
49 + /// The third level MNW's feed has and this fixture did not: the loop is
50 + /// not in the spliced shape, it is in a shape that spliced shape INCLUDES,
51 + /// under a guard. So the branch is numbered in one scope and the loop
52 + /// inside it in another, and the marks reach the region through two
53 + /// boundaries rather than one.
54 + #[staged]
55 + pub(crate) shape rows(feed: &Feed) -> Node;
56 +
57 + list {
58 + for item in feed.items.iter() {
59 + row "{item}";
60 + }
61 +
62 + // A described pager, which is a loop inside an argument's body rather
63 + // than inside a container's. MNW's feed has one here, and it is the
64 + // last structural feature this fixture was missing.
65 + more Rest::page(0, 10).of(feed.items.len()) {
66 + for &page in feed.pages.iter() {
67 + jumping Jump::new(page, Action::get("/feed?page={page}").navigating()) {
68 + here when feed.here(page);
69 + }
70 + }
71 + }
72 + }
30 73 }
31 74
32 75 declare! {
@@ -43,9 +86,9 @@
43 86
44 87 empty "Nothing here yet." when feed.items.is_empty();
45 88
46 - for item in feed.items.iter() {
47 - text "{item}";
48 - }
89 + // A guarded include of a shape that loops, which is the feed's shape and
90 + // the one that was never exercised.
91 + include rows(feed) unless feed.items.is_empty();
49 92 }
50 93
51 94 declare! {
@@ -74,6 +117,7 @@
74 117 Feed {
75 118 heading: heading.to_owned(),
76 119 items: items.iter().map(|item| (*item).to_owned()).collect(),
120 + pages: vec![1, 2, 3],
77 121 }
78 122 }
79 123
@@ -113,8 +157,10 @@
113 157 let residual = residual();
114 158 let (mut branches, mut loops) = (0, 0);
115 159 count(residual.ops(), &mut branches, &mut loops);
116 - assert_eq!(branches, 2, "{:#?}", residual.ops());
117 - assert_eq!(loops, 1, "{:#?}", residual.ops());
160 + // Two guards in the spliced shape, one loop over the rows the shape it
161 + // includes draws, and one over the pager's jumps.
162 + assert_eq!(branches, 3, "{:#?}", residual.ops());
163 + assert_eq!(loops, 2, "{:#?}", residual.ops());
118 164
119 165 // And the caller's own members are outside all of it.
120 166 let settled: String = residual
@@ -410,9 +410,10 @@
410 410 let mut arms = Vec::with_capacity(of.saturating_sub(1));
411 411 for arm in 1..of {
412 412 let (drawn, covers) = render(&plan.clone().with_arm_at(cover.scope, cover.id, arm));
413 - let Some(here) = covers
413 + let Some((at, here)) = covers
414 414 .iter()
415 - .find(|other| other.scope == cover.scope && other.id == cover.id)
415 + .enumerate()
416 + .find(|(_, other)| other.scope == cover.scope && other.id == cover.id)
416 417 else {
417 418 panic!(
418 419 "dispatch {:x}/{} drew arm {arm} nowhere, so the screen has an arm \
@@ -427,7 +428,7 @@
427 428 cover.scope,
428 429 cover.id
429 430 );
430 - let inside = spans_within(&covers, here);
431 + let inside = spans_within(&covers, at);
431 432 arms.push(build(&drawn, here.at, here.to, &inside, &mut 0));
432 433 }
433 434 arms
@@ -438,16 +439,30 @@
438 439 /// A nested dispatch inside an arm is left alone: it is one more site with its
439 440 /// own arms, and it is compiled when the walk reaches it in the render it was
440 441 /// drawn in.
441 - fn spans_within(covers: &[Cover], outer: &Cover) -> Vec<Span> {
442 + fn spans_within(covers: &[Cover], at: usize) -> Vec<Span> {
443 + let outer = &covers[at];
442 444 covers
443 445 .iter()
444 - .filter(|cover| {
445 - !std::ptr::eq(*cover, outer)
446 + .enumerate()
447 + .filter(|(index, cover)| {
448 + // Recorded EARLIER than the arm, which is the same rule `tree` uses
449 + // and the reason it is needed here. A container adds its marks
450 + // innermost first, so a cover recorded after this one encloses it
451 + // rather than sitting inside it -- and range alone cannot tell the
452 + // two apart, because a loop running one pass covers exactly the
453 + // bytes of the single member that pass drew.
454 + //
455 + // MNW's feed is the site: a pager's jump is one member, the loop
456 + // over jumps runs once while staging, and the `here` setting makes
457 + // an arm of that same member. Adopting the loop as the arm's child
458 + // put a `Loop` inside an `Arms`, which the fill program has no
459 + // instruction for.
460 + *index < at
446 461 && cover.at >= outer.at
447 462 && cover.to <= outer.to
448 463 && (cover.scope, cover.id) != (outer.scope, outer.id)
449 464 })
450 - .map(|cover| Span {
465 + .map(|(_, cover)| Span {
451 466 at: cover.at,
452 467 to: cover.to,
453 468 varies: cover.varies,