Skip to content

Scope the chrome's own keycap rules and read one box statement from both sides - #152

Merged
max-sixty merged 3 commits into
mainfrom
fix/ci-33340110090
Aug 31, 2026
Merged

Scope the chrome's own keycap rules and read one box statement from both sides#152
max-sixty merged 3 commits into
mainfrom
fix/ci-33340110090

Conversation

@leaf-agent

@leaf-agent leaf-agent commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Problem

test_a_coined_class_cannot_reach_the_chromes_rules pins the set of lf- classes the chrome styles at document level, and asks that widening it be a decision rather than a side effect. Two names entered it unremarked and it has been red on every run since, 33340110090 included:

AssertionError: the document-level class surface changed: widen the shared vocabulary on purpose
+     'lf-key-more',
+     'lf-key-sequence',

They arrived with 360c40a "Clarify keyboard chord progress", through one rule: .lf-address, .lf-key-sequence > kbd, .lf-key-more kbd stated the keycap geometry for all of them at once, and .lf-address is worn in the page as well as the chrome, so that rule cannot go inside the @scope block. Its two chrome-only companions came out to document level with it.

Solution

Lift the box into one KEY_BOX statement the module interpolates twice: .lf-address reads it at document level, where it has to be, and .lf-key-sequence > kbd, .lf-key-more kbd reads the same statement from inside @scope (.lf-chrome), along with .lf-key-sequence and the pressed-state rule. "One sequential step keeps one geometry" stays literally true — there is still exactly one statement of the box — and the pin closes with no names added.

That is better than widening the pin, which is what this PR did first. The pin is the only thing standing between a page coining lf-key-sequence and the whole keycap box landing on it; the test's other half, the probe wearing every scoped class, drops document-level names by construction and so cannot cover a widened set. Scoped, those two names are on that probe, and it reports no moved property — the coin is now inert for a reason the suite checks rather than for a reason the list asserts.

Nothing in the layer renders differently.

Testing

Why these two names are the chrome's own, measured

Checked rather than assumed, on the corpus example served through the ordinary fixture:

selector after g after g h after g f with ? open
.lf-key-sequence 9, all in .lf-chrome 3, all in .lf-chrome 10, all in .lf-chrome 24 elements over both key classes, 0 outside .lf-chrome
.lf-key-more 1, in .lf-chrome
.lf-address 16, all in the page 16 16

Which matches the code: keylineEl is appended to chromeRoot (leaf.js), and addressChip — the one place a keySequence goes inside an .lf-address chip — puts its chips in addressLayer, which runtime/keyboard/address.js calls "the chrome's layer" at the line that draws them. The page-side .lf-address elements are an option's own corner address, which carries no sequence. With the reference open, every kbd under those classes still computes height: 19px and a 1px border, which is KEY_BOX reaching them from inside the scope.

After the move, the test's own probe reads lf-key-sequence and lf-key-more as scoped-and-not-global, so both are on the element it plants in the page, and moved is empty.

What this leaves red, and one note for #134

That run's other 29 failures are all owned; the map is in a comment on #133, measured on main and on #148's head. #148 takes 19, #137 four, #147 (now #151) two, #134 the catalog digest, and two did not reproduce at -n0. The one still without an owner is the coarse-pointer resize test above.

This branch edits assets/runtime/chrome-style.js, which is one of the 142 files example-previews.py digests, so it re-stales the manifest and belongs before #134 in the merge order that issue describes.


Automated fix for failed run

@leaf-agent

Copy link
Copy Markdown
Collaborator Author

The split does not have to copy the geometry, so the rule's comment may not rule it out. The declarations can be one shared string in the module and two selectors reading it — .lf-address { ${KEY_BOX} … } at document level, and .lf-key-sequence > kbd, .lf-key-more kbd { ${KEY_BOX} display: inline-flex; … } plus .lf-key-sequence and the pressed-state rule moved inside @scope. "One sequential step keeps one geometry" stays literally true: there is still exactly one statement of the box, and the pin closes with no names added, since your own measurement is what says the chrome's two are never in the page.

I built it to check rather than to propose it, and it measures as a no-op: computed style plus rendered width and height for .lf-key-sequence > kbd in both key states, .lf-key-then, .lf-key-more kbd, and .lf-key-sequence itself, read with the g chord armed and again with the full reference open, came back byte-identical before and after. test_a_coined_class_cannot_reach_the_chromes_rules passes with the pin's set untouched, and uv run pytest tests is 740 passed, 6 skipped.

It is a runtime file rather than a test file, so it is the more expensive of the two — chrome-style.js is one of the 142 preview-digest inputs (#133), and it wants a look at a drawn key line that a green suite does not give. Your call which is worth it; I am not opening a competing PR. The diff is below if it is.

The diff, against 521dfec
--- a/skills/leaf/assets/runtime/chrome-style.js
+++ b/skills/leaf/assets/runtime/chrome-style.js
@@
+/* One sequential step's box, stated once and read from both sides of the scope line.
+   The step is the same shape wherever it stands, and it stands in an option's corner as
+   well as in the chrome, so the declarations are shared text rather than a shared
+   selector: naming the chrome's own key classes beside .lf-address put lf-key-sequence
+   and lf-key-more into the document-level vocabulary, where a page coining one of those
+   names would have been dressed by it. */
+const KEY_BOX = `box-sizing: border-box;
+    min-width: var(--lf-key-box); height: var(--lf-key-box); padding: 0 4px;
+    border: 1px solid var(--border-2); border-radius: 4px; background: var(--chip);
+    color: var(--ink-2); font-family: var(--mono); font-size: var(--t-6);
+    line-height: 1.478; text-align: center; white-space: nowrap;`;
+
 /* The private comment-layer stylesheet. The public runtime supplies the
    declaration-derived names and layout queries interpolated into it. */
 export function chromeStyle({
@@
-  /* One sequential step keeps one geometry in options, the key line, page addresses and
-     the reference. A sequence composes these fixed boxes instead of making spaces inside
-     one keycap carry time. A compact choice label such as g / G remains one decision point. */
-  .lf-address, .lf-key-sequence > kbd, .lf-key-more kbd { box-sizing: border-box;
-    min-width: var(--lf-key-box); height: var(--lf-key-box); padding: 0 4px;
-    border: 1px solid var(--border-2); border-radius: 4px; background: var(--chip);
-    color: var(--ink-2); font-family: var(--mono); font-size: var(--t-6);
-    line-height: 1.478; text-align: center; white-space: nowrap; }
-  .lf-address { display: none; border-color: var(--accent); background: var(--card);
-    color: var(--accent); z-index: 1; }
-  .lf-key-sequence { display: inline-flex; align-items: center; gap: 3px;
-    white-space: nowrap; }
-  .lf-key-sequence > kbd, .lf-key-more kbd { display: inline-flex; align-items: center;
-    justify-content: center; }
-  .lf-key-sequence > kbd[data-lf-key-state="pressed"] { border-color: var(--accent);
-    background: var(--accent); color: var(--card); }
+  /* An address chip is the one step box the page also wears — in an option's corner —
+     so it is the one that belongs to the shared vocabulary. The chrome's own sequences
+     take the same box from KEY_BOX inside the @scope block below. */
+  .lf-address { ${KEY_BOX}
+    display: none; border-color: var(--accent); background: var(--card);
+    color: var(--accent); z-index: 1; }
@@ inside @scope (.lf-chrome), beside the key line's other rules
     .lf-keyline .lf-chord-control { margin-inline-start: 4px;
       padding-inline-start: 12px; border-inline-start: 1px solid var(--rule); }
+    /* The chrome's own step boxes: the key line's sequences and its More key, the
+       reference's routes, and the sequence inside an address chip. A sequence composes
+       these fixed boxes instead of making spaces inside one keycap carry time; a
+       compact choice label such as g / G remains one decision point. Every element
+       wearing these stands inside the runtime's container, so the rules do too — the
+       box itself is KEY_BOX, which .lf-address states at document level. */
+    .lf-key-sequence > kbd, .lf-key-more kbd { ${KEY_BOX}
+      display: inline-flex; align-items: center; justify-content: center; }
+    .lf-key-sequence { display: inline-flex; align-items: center; gap: 3px;
+      white-space: nowrap; }
+    .lf-key-sequence > kbd[data-lf-key-state="pressed"] { border-color: var(--accent);
+      background: var(--accent); color: var(--card); }
     .lf-key-more { display: inline-flex; gap: 5px; align-items: baseline; flex: none;

Measured on 2d46d4a, where .lf-key-then was still document level too and moved with them; on 521dfec aa41f7a has already taken it out, so it drops from the hunk.

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The widening itself checks out: .lf-address, .lf-key-sequence > kbd, .lf-key-more kbd and the three rules under it sit above the @scope (.lf-chrome) block in chrome-style.js, the three keySequence call sites (keyline.js, reference.js, address.js) all build into keylineEl / helpEl / addressLayer, and all three are children of chromeRoot. The test passes on the merged tree.

One claim in the new comment is the wrong way round, though, and it is the one doing the safety work — worth fixing because it tells the next reader the pin is redundant.

Comment thread tests/test_render_conversations.py Outdated
The comment justifying lf-key-sequence and lf-key-more in the shared
vocabulary claimed the moved-property assertion above already holds that a
document-level name touches nothing in the page. It holds the opposite side:
its probe is `[...scoped].filter(c => !global_.has(c))`, so every name in the
document-level set is dropped from it by construction — including the two this
change adds. The names are not inert either; an element in the page coining
them would wear the whole keycap box. What makes the widening safe is that
nothing in the page wears them, and this pin is the only check on that.
@max-sixty
max-sixty enabled auto-merge (squash) August 31, 2026 00:51
@leaf-agent leaf-agent changed the title Name the chord's document-level keycap classes in the chrome vocabulary pin Scope the chrome's own keycap rules and read one box statement from both sides Aug 31, 2026
@leaf-agent

Copy link
Copy Markdown
Collaborator Author

Taken, and both points hold. 8ffdfc2 is the split: KEY_BOX is one statement at module scope, .lf-address reads it at document level, and .lf-key-sequence > kbd, .lf-key-more kbd reads the same statement inside @scope, with .lf-key-sequence and the pressed-state rule moved in beside them. The pin closes with no names added, so the widening and its comment are gone and 40a76a7's correction to that comment goes with them — the branch's net diff against main is now chrome-style.js alone.

The correction is what decided it. My comment claimed the moved-property assertion already held that a document-level name touches nothing in the page; it cannot, for exactly the reason given — [...scoped].filter(c => !global_.has(c)) drops every name in the widened set. So widening would have left the pin as the only check on a fact the suite could otherwise make. Scoped, the two names read scoped/- from that same probe, land on the element it plants in the page, and produce no moved property: the fact is checked rather than asserted.

Measured that the box is unchanged rather than describing it: on corpus with the chord up, sixteen computed properties on .lf-key-sequence > kbd, .lf-key-more kbd and .lf-address are identical before and after, and the sequence keycap's getBoundingClientRect is the same to the pixel. With ? open, all 24 elements over both classes are inside .lf-chrome and every kbd still computes height: 19px with a 1px border, which is KEY_BOX reaching them from inside the scope.

@max-sixty
max-sixty merged commit 6d68a1b into main Aug 31, 2026
3 checks passed
@max-sixty
max-sixty deleted the fix/ci-33340110090 branch August 31, 2026 01:04
max-sixty pushed a commit that referenced this pull request Sep 3, 2026
## Problem

The nightly leg of `ci` failed on [run
33735071949](https://github.com/max-sixty/leaf/actions/runs/33735071949)
(`a57ae29`) with seven cases. Six are already owned by open PRs:
`test_a_widgets_label_takes_a_comment_inside_the_control_it_labels`,
`test_a_closed_leaf_clears_itself_off_the_tray` and
`test_the_leaves_tray_takes_the_keyboard` by #236; the ring walk by
#239's `ship-review` precondition standing ahead of #240's
`lf-shotframe` fault; and both of
`test_page_fixture_renders[pr-walkthrough]` (the 4px sideways scroll on
`pr-scale-groups`) and
`test_nested_command_projections_stop_at_their_own_boundary` (`§
inner-worker`) by #243.

The seventh is new with `a57ae29` and nobody's yet:

```
FAILED tests/test_render_conversations.py::test_a_coined_class_cannot_reach_the_chromes_rules
AssertionError: the document-level class surface changed: widen the shared vocabulary on purpose
  Extra items in the left set:
  'lf-workspace'
```

#242 wrote the open workspace's tint as `.lf-btn.on,
.lf-btn.lf-workspace[aria-expanded="true"]` in `chrome-style.js`, at
document level. `lf-workspace` is worn by three controls and all three
are the chrome's own — the Threads toggle built at
[`leaf.js:817`](https://github.com/max-sixty/leaf/blob/064e67d5/skills/leaf/assets/leaf.js#L817),
and All leaves and Asks, which `trayIs` marks at
[`trays.js:194`](https://github.com/max-sixty/leaf/blob/064e67d5/skills/leaf/assets/runtime/trays.js#L194).
Every one of them is inside `chromeRoot`, the `.lf-chrome` container the
private stylesheet is scoped to. So the name reached document level
without being shared vocabulary, which is the seam the pinned set exists
to catch — the same one #152 answered for the keycap rules.

## Solution

Move the selector into the `@scope (.lf-chrome)` block, beside
`.lf-banner-more[data-lf-news]`, which paints the same accent face for
the same kind of banner state. `.lf-btn.on` keeps its document-level
rule, unchanged and now on its own: `on` is worn out in the page and is
the reason that line was there before #242.

Nothing about the paint moves. Specificity is unchanged, and the one
rule that could contend — `.lf-banner-menu > .lf-btn { border-color:
transparent }`, for a workspace control folded into the overflow menu —
is `(0,2,0)` against this selector's `(0,3,0)` on both sides of the
move, so scope proximity never gets a say. Fixing it at the pinned set
instead would have widened the shared vocabulary by a class no widget
can wear, which is what the assertion's own message asks you not to do.

## Testing

On this branch, rebased on `fdba9e1d`:

- `uv run pytest tests` — 808 passed, 6 skipped
- `test_a_coined_class_cannot_reach_the_chromes_rules` and both
parameters of #242's
`test_workspaces_replace_each_other_and_name_the_open_one` pass
- `uv run pytest tests/test_render_conversations.py
tests/test_render_controls.py --run-nightly` — 141 passed, and the three
failures are #236's two tray cases and the ring walk, all reproducing
identically on `main`

Not vacuous: deleting the scoped rule fails the workspace test at both
500 and 1200 with the resting face (`rgb(238, 234, 222)` where the
active face is `rgb(255, 254, 251)` under `--chip`) rather than passing
by construction. `chrome-style.js` is served rather than bundled, so
there is nothing to re-vendor, and no user-visible pixel changes, so
there is no before/after shot to hand over.

---
Automated fix for [failed
run](https://github.com/max-sixty/leaf/actions/runs/33735071949)

Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants