Skip to content

fix: /pano overflows the mobile viewport: 500px of document in a 390px window - #9586

Open
usirin wants to merge 2 commits into
mainfrom
build/7730-subnav-mobile-reflow-c9b85c29
Open

usirin wants to merge 2 commits into
mainfrom
build/7730-subnav-mobile-reflow-c9b85c29

Conversation

@usirin

@usirin usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member

/pano laid out 500px wide inside a 390px viewport, so a phone got horizontal scroll on the
product's main feed. The width came from the shared Subnav: .kp-subnav is a nowrap flex row
whose every zone sizes to its own content, so the row's min-content width is the sum of every
label plus every padding. pano publishes five destinations plus a CTA, that sum is wider than a
phone viewport, and a block in normal flow that wide grows the document's scroll width.

The reflow that fixes that is the sibling chrome row's idiom (Topbar.css's own ≤640px rule),
landed in Subnav.css's existing @media (max-width: 640px) block: the bar and its nested tab
strip wrap, height goes auto over a min-height floor, and gap and padding tighten. That half
is unchanged and stays.

The subnav stays sticky on phones

Round 1 of this PR rested the bar in normal flow below 640px, on the reasoning that its correct
sticky offset is a height no stylesheet can read. The founder ruled against that
(ruling): the bar stays
sticky at every width, and the offset follows the topbar's real height. This round does that, and
the position: static is gone.

Nothing in CSS can read another element's height, so the two bars publish their own.
stickyChrome.ts gives each of them a ref that measures its border box and writes it to the
document root — --kp-topbar-measured-h, --kp-subnav-measured-h — behind a ResizeObserver, so
the value re-publishes on every change to the box rather than being read once at load. That one
mechanism covers the wrap at 640px, a viewport resize in either direction, text zoom and a changed
root font size without enumerating any of them. Three rules consume it:

  • .kp-subnav's top is var(--kp-topbar-measured-h) — measured, at every width.
  • html's scroll-padding-top is the two measured heights added, so a focused element or an
    in-page anchor target never comes to rest under the bars (WCAG 2.4.11).
  • stickyChrome.css declares what both properties resolve to before the first measurement and
    after a bar unmounts: the topbar's own one-row token, and nothing at all for an absent subnav.
    Neither default is a guess at a wrapped bar's height, and no rule in the change hard-codes one.

getBoundingClientRect rather than offsetHeight because the measurement is fractional — the
topbar reads 105.796875px at 320px, and a value rounded to a whole pixel leaves a hairline of
topbar background showing between the bars.

LAW-SOURCE: manifest-prose — this repo declares no design-prohibitions.json, so the manifest's
prose prohibitions are the law this was built to. The diff adds no color, no type and no raw
spacing value: every declaration is a layout keyword, an existing role token, or a measured
runtime property.

Hand verification

ui render cannot stand /pano up in this checkout: the web surface row starts the worker
through alchemy dev, whose plan fails with Unauthorized: Authentication error against
Cloudflare here. That is exit 11 — UNKNOWN, not a proven render outcome. So the geometry was
verified by hand instead, on a desk running the app's own vite dev server and driving real Chromium
over /pano at four viewports, reading the numbers back off the rendered page. Two things about
that desk are synthetic and nothing else is: the feed is empty because the API is down, and the
driver appends a tall block of focusable rows inside <main> so the document scrolls and has tab
stops below the chrome. The bars, their stylesheets and the measurement are the real ones.

at doc width subnav top resolves to topbar height gap between bars scroll-padding-top
320px 320px, no h-scroll sticky 105.797px 105.8px (wrapped) 0px 145.797px
390px 390px, no h-scroll sticky 101px 101px (wrapped) 0px 141px
641px 641px, no h-scroll sticky 38px 38px (one row) 0px 70px
1280px 1280px, no h-scroll sticky 38px 38px (one row) 0px 70px

top equals the measured topbar height at every row, and the gap is exactly zero — the bars are
flush, with no overlap and no strip of topbar background between them. After scrolling 900px the
subnav is still pinned with the same zero gap at all four widths, so it no longer scrolls away with
the feed. Desktop is untouched: 641px and 1280px read the same 38px offset and 32px bar height they
read before the change.

The focus half was measured against the same desk, and it is worth stating what the comparison
shows, because the defect is pre-existing rather than introduced here. Tabbing backward up the
feed is the direction that scrolls a stop to the viewport's top edge, where the bars are. On
origin/main — sticky bar, no scroll-padding-top — focus landed at top: 46.8px and then
top: -0.2px against a bar whose bottom edge is at 70px: hidden under the chrome, a WCAG 2.4.11
failure at every viewport including desktop. An in-page anchor landed at -0.2px, the same way.
With this change, across all four viewports, the lowest a backward tab stop came to rest is
178.8px at 390px against a bar bottom of 141px, and the anchor target lands at 140.8px
against the same 141px — clear of the bar in every case, and nothing is hidden.

Unit tests hold both halves in source, matching how Subnav.css's existing rules are held: the
reflow is inside the one existing breakpoint (a second @media fails the assertion), both rows
wrap, and the narrow block declares neither position nor top while the base rule sticks off
the measured property above it. stickyChrome.test.tsx drives the hook against a firing
ResizeObserver — it publishes the rendered height on mount, each bar to its own property,
re-publishes when the box changes, and withdraws the property on unmount — and pins the two
stylesheet rules the offsets depend on. apps/web client tests for src/components/layout are
green (69 tests) and build check --surface code is green.

Two criteria stay owed to the design gate rather than answered here: the
review-ui render --viewport mobile captures of /pano:auth and /pano:auth-caylak, which need
this PR's preview deployment, and the signed-in bar's five destinations plus the CTA, which the
signed-out desk does not render. Both are measured against a preview this lane cannot deploy.

Deviations

  • Known defect left unfixedSaid: the issue asks for a narrow-viewport capture of
    /pano as the check that it worked. Did: attached no ui render captures; the verb exits 11
    because the local worker's alchemy dev plan fails Cloudflare auth in this checkout, and
    review-ui render needs a deployed preview that does not exist until this PR's own gate runs.
    Why: exit 11 is UNKNOWN, so recording it as a render would be claiming a look nobody took.
    Disposition: the geometry proof is the hand-verification desk above, driving real Chromium
    over the real app; the preview capture is the design gate's to take.
  • Scope narrowingSaid: criterion 1 names signed out and signed in, and criterion 2
    names the five signed-in destinations plus the CTA. Did: verified the signed-out bar only.
    Why: the desk has no working API, so no session can be established on it. Disposition:
    stated here; the reflow rules those criteria rest on are unchanged this round and were graded on
    the diff in round 2, and the signed-in render is the design gate's at the preview.
  • Out-of-scope changeSaid: the reflow lives in apps/web/src/components/layout/Subnav.css.
    Did: also added stickyChrome.ts and stickyChrome.css beside it and attached a ref in
    Topbar.tsx and Subnav.tsx. Why: the ruling requires a measured topbar height, and no
    stylesheet can read one — the measurement has to come from the component that renders the bar,
    and scroll-padding-top belongs on the scrolling element, which is html. The reflow itself is
    still entirely in Subnav.css. Disposition: stated here and held by unit tests.
  • Out-of-scope changeSaid: the issue is pano's overflow. Did: also changed how
    sözlük's alphabet strip renders at ≤640px — it used to spill outside the 32px bar and across
    the bar's own border, and the bar now grows to contain it. Why: the fix is on the shared
    bar, and height: auto cannot be scoped to one consumer; the criteria ask for every
    SubnavShell consumer to be checked on the shared bar. Disposition: stated here; it is a
    strict improvement, not a regression, and it is unchanged by this round.
  • Known defect left unfixedSaid: nothing about desktop focus. Did: the
    scroll-padding-top this change adds also fixes a pre-existing WCAG 2.4.11 failure at 641px and
    wider, where a backward tab stop or an anchor target landed under the sticky bar on origin/main.
    Why: the offset is measured from the same two properties at every width, and scoping the
    scroll padding to phones only would have left the desktop defect standing for no reason.
    Disposition: fixed rather than left, measured in the section above.
  • Declined guidanceSaid: Topbar.css:276-301 is the idiom to follow, and it only drops
    its spacer. Did: dropped the spacer and added margin-left: auto on .kp-subnav__cta.
    Why: the Topbar has no trailing-edge primary action; the Subnav does (Decision: the placement law — where each nav element class lives #2587), and without
    the auto margin the CTA left-jams against the signal on the wrapped line. Disposition:
    stated here, and held by a unit test.
  • Pre-existing test or fixture changedSaid: nothing about the existing test file.
    Did: added a narrowBlock helper in Subnav.test.tsx in an earlier round, and this round
    replaced that file's rests the bar in normal flow assertion with its opposite — the base rule
    sticks off the measured property, and the narrow block declares neither position nor top.
    Why: the assertion the earlier round added pinned the behaviour the founder ruled against, so
    leaving it would have red the suite against the ruling. Disposition: replaced, not deleted;
    every other assertion in that file is untouched and still passes.
  • Guard or gate bypassedSaid: build check's green names Subnav.css and
    stickyChrome.css in unvalidated. Did: ran no second surface for them. Why: the
    surfaces are code, prose, plan and workflows, and none of them validates a .css file beyond the
    guards the code run already swept over it (design-token-guard, design-inventory).
    Disposition: stated here; CI's own gates answer it.

Fixes #7730

…7730)

The Subnav is a nowrap flex row whose every zone sizes to its own content, so its
min-content width is the sum of every label plus every padding. pano publishes five
destinations plus a CTA, that sum is wider than a phone viewport, and a block in
normal flow that wide grows the document's scroll width — which is the 500px the
report measured inside 390px.

Wrap the bar and its nested tab strip at the file's existing ≤640px breakpoint,
following the Topbar's own phone rule. The sticky offset goes with it: `top:
var(--topbar-h)` is only honest while the topbar is one row, and the topbar's ≤640px
rule deliberately wraps search onto a second row, so below the breakpoint the bar
rests in normal flow instead of carrying a height no stylesheet can read.

Measured on a headless desk at 390/641/1280 against both Subnav.css revisions: pano's
document scroll width drops 521px → 390px, every destination and the CTA stay on
screen and unclipped, and 641px and above are byte-identical in bar height, position,
offset and zone geometry.
@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

review-code: PASS @ ac023ad content:29fb74284131 — CI is green at this head and every code-class criterion is discharged; the rendered capture stays owed to review-ui

Round 2, head ac023ad2734ebab9bc0664021dd883981cc92646. The standing verdict at this head is round 1's
review-code: FAIL, and it failed on execution evidence alone — CI red, unit + client tests
unconcluded. Both are cleared at this same head; no code change was needed and none was made.

What changed since round 1

review ci --wait now settles green at ac023ad2: 49 check runs, 43 success, 6 skipped, no
failure and nothing in flight; 28 of 43 repo-authored workflows inspected this head, so the gate
coverage is real. The blocking e2e job's auth.setup.ts fixture — the #8659 symptom that reddened
round 1 — passed on re-run, confirming it was the spec-side flake round 1 identified rather than
anything this diff caused. unit + client tests concluded green, so criterion 8's new assertions
are proven at head, not merely well-shaped.

Round 1's third finding (mecmua unmeasured) I re-read rather than inherited, and it does not
survive as a gap: MecmuaSubnavLayout.tsx composes at most three flag-gated destinations plus
MecmuaSubnavCta — strictly narrower than pano's five plus a CTA, through the same two
SubnavShell zones the wrap rules cover — and criterion 6 names sözlük, divan and the
ProductSubnavLayout substrate, all three of which the PR body measures. A note, not a finding.

Criteria

None of the 8 rows carries an outside-diff evidence marker, so each is graded on the diff, read out
of the scoped head.

  1. 390px, no horizontal scroll, signed out and in — discharged. The overflow floor is
    .kp-subnav's nowrap flex row summing every zone's min-content width; the block adds
    flex-wrap: wrap to the bar and to the nested .kp-subnav__filters, with min-width: 0 on
    the strip and each filter. That removes the floor rather than hiding it — there is no new
    overflow anywhere in the block. Auth state only changes how many destinations render, and the
    rule is indifferent to the count.
  2. Five destinations plus the CTA all reachable — discharged. The block's only display: none
    is on .kp-subnav__spacer, a zero-content flex spacer. Nothing else is hidden, clipped or
    truncated; the filters wrap onto a second line at their own --subnav-h height.
  3. Inside the file's existing @media (max-width: 640px), following the Topbar idiom
    discharged and held. That block is the head's only @media in Subnav.css, and the new test
    asserts the matched at-rule set equals exactly one entry, so a second breakpoint reds. The
    declarations mirror Topbar.css's ≤640px rule: height: auto over a min-height floor,
    flex-wrap, tightened gap/padding, spacer dropped.
  4. Sticky offset stays correct across a reflow — discharged, by resting the bar rather than
    re-computing the offset: below 640px .kp-subnav goes position: static; top: auto, so no
    stale top: var(--topbar-h) survives against a topbar whose own ≤640px rule wraps search onto a
    second row. Both failure modes the criterion names are then unreachable in CSS alone. I checked
    what the change could strand: --subnav-h appears only in tokens.css (declarations),
    Subnav.css itself, its test, and ToggleGroup.css's min-height — nothing positions off a
    subnav that no longer sticks, and z-index: 49 going inert under static strands nothing.
    See the note below on what this costs.
  5. Desktop unchanged at ≥641px — discharged. Every added declaration sits inside the media
    block, and the test pins the base position: sticky; top: var(--topbar-h) as still present in
    the source above that block.
  6. Every other SubnavShell consumer at 390px — discharged for the three the criterion names.
    The PR body measures pano, sözlük and divan on a headless desk over the real token and layout
    stylesheets at 390 / 641 / 1280px, and the sözlük result is consistent with the stylesheets:
    .kp-subnav__filters .kp-sozluk-alphabet { height: 100% } now resolves against an auto
    parent, which is what lets the bar grow to contain the 29-letter strip instead of the strip
    spilling across the bar's border. Mecmua is covered by the same shared rules (note above).
  7. review-ui render --viewport mobile captures /pano:auth and /pano:auth-caylak at 390px
    not graded here. review scope routes review-ui, pixels are that gate's modality, and the
    criterion stays owed there. Nothing in this verdict discharges it, and its absence from this
    namespace is not a gap in mine.
  8. A unit test holds the narrow-viewport rule in the stylesheet source — discharged in shape
    and now in result. Subnav.test.tsx's Subnav narrow-viewport reflow (#7730) block
    brace-matches the media block off the source and asserts inside it, which is the right call:
    a regex over the whole file would pass on a position: static that escaped the query and
    unstuck the desktop bar. I re-derived narrowBlock on this file: it seeks the first { at or
    after the at-rule (the prelude carries no brace), unwinds on depth, and throws rather than
    returning a prefix on an unterminated block. Each .kp-subnav assertion requires the brace
    immediately after the selector, so .kp-subnav__filters cannot satisfy a .kp-subnav one. It
    matches the file's existing source-assertion idiom, and it is green at head.

Note (non-blocking, and one for the rendered gate)

The mobile subnav no longer sticks — it scrolls away with the feed below 640px. That is a real
behaviour change on a rendered surface, deliberate, reasoned in the stylesheet comment and stated
twice in the PR body, and it satisfies criterion 4 as the criterion is written (the bar cannot
overlap the topbar and the feed cannot slide beneath it). Whether a non-sticky mobile subnav reads
right is review-ui's call at the preview, not this namespace's, so I name it here rather than
grading it: if that gate wants the bar to keep sticking on a phone, the offset needs a height no
stylesheet can read today, and that is a different shape than this change.

No acceptance criterion appended this round.

Deviations

deviations found — the section is present with five entries and their substance lines up with the
diff I read. The two that matter are on record: the departure from the Topbar idiom (spacer dropped
and margin-left: auto added on .kp-subnav__cta, for the trailing-edge placement law #2587,
which the Topbar has no equivalent of, held by a unit test), and the out-of-scope sözlük strip
change that height: auto cannot be scoped away from. The missing /pano capture is disclosed as
exit 11 / UNKNOWN rather than dressed up as a render, which is the honest call. The sticky rest is
disclosed in the body's opening section and in the stylesheet comment rather than as a table entry;
I read that as disclosed, not hidden. Nothing I found is undisclosed.

deviation-disclosure: PASS — nothing undisclosed that this gate could see.

Verdict-written: 2026-09-21T01:06:32Z

Superseded verdict — 2026-09-21

review-code: FAIL @ ac023ad content:29fb74284131 — CI is red at this head: the blocking e2e job's auth setup fixture failed on both attempts, and unit + client tests never concluded

Round 1, head ac023ad2734ebab9bc0664021dd883981cc92646, no prior verdicts at this head.

The change itself reads right. It fails on its execution evidence: CI is red at this head.

Findings

1 — blocking — CI is red at the scoped head.
review ci --wait settled red. Run
35548148138, job
e2e (reads + authed + flows, blocking): the Playwright [setup] auth.setup.ts:18 authenticate
fixture failed on the first attempt and on its retries: 1 retry:

Error: username bootstrap gate still mounted after 10000ms with no .kp-auth__error rendered
  at expectGateCleared (apps/web/tests/e2e/_helpers/auth.ts:92)
  at completeBootstrap (apps/web/tests/e2e/_helpers/auth.ts:139)
  at apps/web/tests/e2e/_setup/auth.setup.ts:23

1 failed, 1 did not run, 90 passed (6.3m). The preview-readiness gate passed before the specs
ran, so this is a spec-side failure rather than a cold preview.

On inspection the failing fixture is the authed project's setup and this PR's two files touch no
auth surface — it matches #8659 ("The blocking e2e gate's auth setup times out on the
username-choice step, reddening unrelated PRs") symptom for symptom, down to the error string that
issue's fix installed. That is an explanation, not a discharge: a red head is a red head and no
namespace passes over one, so the round fails on it. The repair move is a re-run of the blocking
job at head — and heal-ci if it repeats — not a code change.

2 — blocking (same head, unread) — unit + client tests never concluded.
At settle that check was still in_progress (48 check runs: 40 success, 6 skipped, 1 failure,
1 in progress). So criterion 8's new assertions are UNKNOWN at this head, not green: their
shape is graded below, their result is not.

3 — non-blocking — a fourth SubnavShell consumer is unmeasured.
apps/web/src/components/mecmua/MecmuaSubnavLayout.tsx composes SubnavShell with destinations
plus MecmuaSubnavCta, the same two zones pano uses, and it is absent from the PR body's
verification table (pano / sözlük / divan). The fix is on the shared bar and mecmua carries no zone
the wrap rules miss, so this is a gap in the measurement rather than a suspected regression — but
criterion 6 says every other consumer. Worth a row in the table next round. No acceptance
criterion appended: criterion 6 already carries it.

Criteria

None of the 8 rows carries an outside-diff evidence marker, so each is graded on the diff, read out
of the scoped head.

  1. 390px, no horizontal scroll — discharged in mechanism. The desktop floor is .kp-subnav's
    nowrap flex row; the block adds flex-wrap: wrap to it and to the nested .kp-subnav__filters,
    plus min-width: 0 on the strip and its filters, which is exactly what removes the
    sum-of-labels min-content floor. Nothing is clipped behind an overflow.
  2. All five destinations plus the CTA reachable — discharged. The rules wrap, they do not hide:
    the only display: none in the block is on .kp-subnav__spacer, a zero-content flex spacer.
  3. Inside the file's existing @media (max-width: 640px), following the Topbar idiom
    discharged, and held. That block is the file's only @media in the head's source, and the new
    test asserts the matched set equals exactly one entry, so a second breakpoint reds. The
    declarations mirror Topbar.css's ≤640px rule: height: auto over a min-height floor,
    flex-wrap, tightened gap/padding, spacer dropped, padding-inline: var(--s-2) on the items.
  4. Sticky offset stays correct across a reflow — discharged, by removing the offset rather than
    correcting it: below 640px the bar goes position: static; top: auto, so no stale
    top: var(--topbar-h) is left to be wrong against a topbar that wraps to two rows. I checked
    what that could strand: across apps/web/src and packages/design/src, --subnav-h and
    --topbar-h appear only in tokens.css (declarations), ToggleGroup.css (a min-height) and
    the two layout stylesheets themselves — nothing positions off a subnav that no longer sticks,
    and z-index: 49 going inert under static strands nothing either. The real change riding
    along is that the mobile subnav now scrolls away with the feed; it is disclosed twice in the PR
    body and once in the stylesheet comment, and whether it reads right is the rendered gate's call,
    not this namespace's.
  5. Desktop unchanged at ≥641px — discharged. Every added declaration sits inside the media
    block, and the test pins the base position: sticky; top: var(--topbar-h) as still present
    above that block.
  6. Every other SubnavShell consumer at 390px — discharged for the three measured
    (pano / sözlük / divan), and consistent with the stylesheets for sözlük specifically:
    .kp-sozluk-alphabet already carries flex-wrap: wrap, and its
    .kp-subnav__filters .kp-sozluk-alphabet { height: 100% } now resolves against an auto
    parent, which is what lets the bar grow to contain the strip instead of the strip spilling
    across the bar's own border. Mecmua is the gap — finding 3.
  7. review-ui render --viewport mobile captures /pano:auth and /pano:auth-caylak at 390px
    not graded here. review scope routes review-ui, pixels are that gate's modality, and the
    criterion stays owed there. Nothing in this verdict discharges it, and its absence here is not a
    gap in this namespace.
  8. A unit test holds the narrow-viewport rule in the stylesheet source — discharged in shape,
    UNKNOWN in result (finding 2). Subnav.test.tsx's Subnav narrow-viewport reflow (#7730) block
    brace-matches the media block off the source and asserts inside it, which is the right call: a
    regex over the whole file would pass on a position: static that escaped the query and unstuck
    the desktop bar. The narrowBlock matcher is correct on this file — it seeks the first { at or
    after the at-rule (the selector prelude holds no brace) and unwinds on depth, and it throws
    rather than returning a prefix on an unterminated block. It matches the file's existing
    source-assertion idiom.

Deviations

deviations found — the section is present with five entries, and their substance lines up with
what I read. The two that matter are on record: the departure from the Topbar idiom (spacer dropped
and margin-left: auto added on .kp-subnav__cta, for the trailing-edge placement law #2587,
which the Topbar has no equivalent of), and the out-of-scope sözlük strip change that height: auto
cannot be scoped away from. The missing /pano capture is disclosed as exit 11 / UNKNOWN rather
than dressed up as a render, which is the honest call. Nothing I found is undisclosed.

deviation-disclosure: PASS — nothing undisclosed that this gate could see.

Verdict-written: 2026-09-21T00:50:51Z

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Driver note — repair round 1 was a CI re-run, not a code change.

The round-1 review-code FAIL stood on a red head, and the red was one job: e2e (reads + authed + flows, blocking), failing in auth.setup.ts on the authed project's setup fixture — a surface this PR's two files do not touch. That is the recurrence the reviewer filed as #9591.

I re-ran the failed jobs of run 35548148138 at the same head (ac023ad2734ebab9bc0664021dd883981cc92646). It settled green. No commit was pushed and the head is unchanged, so every verdict and every passing check bound to that SHA still binds — including unit + client tests, which the round-1 read caught mid-flight as UNKNOWN and which has since passed, and deploy (web, @kampus/web, true), which passed, so a preview exists for the rendered gate.

Recorded as the repair round's DONE with classes code and ui relayed off review scope at that head. Round 2 re-reads the same artifact over a green head.

Counting it as a hand-fixed gate rather than a silent retry: the lane spent a repair round on a flake, which is #9591's cost to price.

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

review-ui: CANT-SEE at ac023ad2734ebab9bc0664021dd883981cc92646

No review-ui verdict is posted. The two surfaces this issue's own acceptance criteria turn on —
/pano:auth and /pano:auth-caylak at the mobile viewport — could not be rendered here, so the
central claim (the five signed-in destinations plus the CTA laying out inside 390px) is UNKNOWN,
and an unseen input blocks a PASS.

The blocker

review-ui render --pr 9586 --surface /pano:auth --surface /pano:auth-caylak --viewport mobile --viewport desktop refused all four tier shots on exit 11:

did not render signed in (the preview answered the seeded cookie as a visitor) — the
authenticated render is UNKNOWN, never the anonymous one.

Both tier session tokens were present in this shell's environment and the resolved
BETTER_AUTH_SECRET was a real 64-character value rather than the insecure_ placeholder, so the
refusal is one of two things and this session cannot tell them apart:

  1. the ambient signing secret is not the one this preview worker verifies against — the readable
    copy lives in the ci-credentials stack's alchemy state, and no ALCHEMY_PASSWORD was available
    here to export it; or
  2. the test accounts were never seeded onto this preview's D1, which is an operator's act
    (preview-seed test-account), not this gate's.

Either way the account was not handed to this run. A direct read of the preview D1 to split the two
was blocked by the harness, so it stays unsplit.

What did render, and how it read

Captured at head ac023ad2 against the PR's own preview, anonymous, 0 page errors unless noted:

surface viewport outcome
/pano mobile captured 390x1340 — no exit-19 width refusal, no horizontal overflow; the bar holds sıcak / yeni / en iyi / tartışma plus the 14 başlık crumb on one line, crumb on the trailing edge
/pano desktop captured 1280x971 — single sticky row, same zone order as before
/sozluk mobile captured 390x844 — the shared bar wraps: three letter lines, the + yeni tanım CTA on its own line and still pinned to the trailing edge, bar resting in normal flow, no horizontal scroll
/sozluk desktop captured 1280x800 — single row, unchanged
/divan mobile + desktop captured, but the page is the yazar/moderator gate's error screen, so no Subnav rendered — divan's bar is unjudged at both widths (2 console errors, both the expected FateRequestError permission message)

So the reflow rules in the diff did paint, and they read clean against the manifest's prose law on
the sözlük bar, which is the heavier case (26 letters plus a CTA). What is missing is pano's own
signed-in row and divan's bar — both behind login.

What the next runner needs

A shell holding the preview worker's real BETTER_AUTH_SECRET (ci-credentials export, behind
ALCHEMY_PASSWORD) and both seeded tier tokens for this preview's D1, then:

review-ui render --pr 9586 --out judged --surface /pano:auth --surface /pano:auth-caylak \
  --surface /divan:auth --viewport mobile --viewport desktop --auth-secret-from <export>

Nothing in the diff is being asked to change on this note. The branch's text gate stands PASS at
this head; this is an evidence gap in the gate, not a defect in the PR.

LAW-SOURCE: manifest-prose — the repo declares no design-prohibitions.json.

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Driver note — lane 7730 leaves its park for a repair round, not a discharge

The recorded park cause is not discharged, and this comment does not claim it is. Lane 7730 sits at blocked --cause no-preview-render. That cause still holds: the signed-in five-tab /pano subnav row — the widest case #7730 is about — still cannot be rendered on a preview by an agent, because the preview auth secret and a seeded account are not available to one (#9339, #9533, #9281, #9288). Nothing below changes that.

What changed is the spec, under a landed verdict. The founder ruled the one design question this PR raised: #7730 (comment)

the subnav stays sticky on phones.

So this PR's position: static below 640px is not accepted. review-code PASS round 2 at ac023ad bound a verdict against acceptance criteria that the ruling has since replaced, and #7730's ### Acceptance criteria now carries the ruling as gradeable rows. A verdict cannot stay binding over criteria it never read.

So what is owed at this head is a repair round. The driver is recording, in this order:

  1. ISSUE.UNBLOCKED out of blocked, returning the task to review:ui — the state the park was recorded from.
  2. A driver-originated ISSUE.FAIL out of review:ui, which spends one of the lane's three repair retries (1 → 2) and routes the task to build:ui.

No reviewer verdict is being asserted, overwritten or faked by this. review-ui is empty at ac023ad and stays empty — the rendered gate has reached no verdict on this head and this comment does not stand in for one. The FAIL is the driver recording that the artifact no longer satisfies the criteria, which is readable off the ruling and this diff alone, with no render behind it. The rendered gate still runs at the repaired head, and if the criteria there still need the signed-in surface, the lane parks again with the exact render command owed.

The FAIL floor in the operate skill — record one only when no derived namespace is still in flight — holds here: the review-ui run that recorded the park has ended, so there is no verdict mid-write for this to orphan.

Precedent for a driver-originated FAIL taken to force an owed repair round: lane 9508 on #9522.

What the repair must hold (the ruling's own terms, now on #7730):

  • The subnav stays sticky on phones. The sideways-scroll reflow this PR landed stays.
  • The sticky offset follows the real topbar height, including when .kp-topbar's own ≤640px rule wraps search onto a second row (height: auto; min-height: var(--topbar-h); flex-wrap: wrap). A measured height exposed as a custom property, or an equivalent that stays correct on wrap, resize, zoom and font-size change. No hard-coded second-row guess.
  • The sticky bar must not hide focused content or in-page anchors — scroll-padding-top or equivalent, WCAG 2.4.11.
  • 320px reflow (WCAG 1.4.10) still holds.
  • It must compose with fix: The sözlük alphabet strip overlaps the page breadcrumb at 390px and clips its yeni tanım button #9598, which landed today and gives .kp-subnav:has(.kp-sozluk-alphabet) a wrapping multi-row phone box: sözlük's taller bar has to stick correctly too, and must not cover content.

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Round 3, head 41ae96f5. This round answers the issue's re-triage, not a gate FAIL: the fold at
ac023ad2 carries one row, review-code: PASS, with no findings, no escalations, capReached: false and a clean merge. What changed is the contract. The founder ruled on the one design
question round 1 raised, triage amended #7730 with eight more criteria, and the first of them names
this PR by number: position: static below 640px is not accepted.

What I changed. The subnav sticks at every width now, offset by the topbar's measured height.
stickyChrome.ts is the new piece — each bar gets a ref that measures its border box and publishes
it to the document root behind a ResizeObserver, so the offset re-derives on wrap, resize, text
zoom and a root-font-size change instead of being read once. .kp-subnav's top reads that
property, and html's scroll-padding-top is the two bars' measured heights added, which is the
WCAG 2.4.11 half. stickyChrome.css declares what both properties resolve to before the first
measurement, so no rule guesses a wrapped bar's height. The sideways-scroll reflow the earlier
rounds landed is untouched.

Where to look first. stickyChrome.ts — it is the whole mechanism, and the one place a stale
offset could come back. Then the narrow block in Subnav.css, which now declares neither
position nor top; Subnav.test.tsx asserts both absences inside the brace-matched block,
because an unstick that escaped the media query is invisible to a regex over the file.

One deliberate widening, disclosed in the body. The scroll-padding-top is not scoped to
phones, so it also fixes a pre-existing desktop defect: on origin/main a backward tab stop and an
in-page anchor both came to rest at top: -0.2px under a bar whose bottom edge is at 70px, at 641px
and 1280px as well as narrow. Scoping the fix to phones would have left that standing for no
reason.

Evidence state, stated plainly. No ui render captures are attached. The verb exits 11 in this
checkout — the web surface starts the worker through alchemy dev, whose plan fails Cloudflare
auth here — and 11 is UNKNOWN, so recording it as a render would claim a look nobody took. Instead
the geometry is measured by hand on a desk running the app's own vite dev server against real
Chromium at 320 / 390 / 641 / 1280px, and the numbers are in the PR body's hand-verification
section, which criterion 14 names as its evidence source. Two criteria stay owed to review-ui at
the preview: the --viewport mobile captures of /pano:auth and /pano:auth-caylak, and the
signed-in five-destination bar, which the signed-out desk does not render.

The standing review-code: PASS was written at ac023ad2 and the head has moved, so it needs
re-running at 41ae96f5.

— at 41ae96f

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

review-code: PASS @ 41ae96f content:25b68d6097b9 — merge-ready

Round 3 at head 41ae96f5. Scope derives review-code and review-ui; review-ui is routed, so
this verdict covers the code class only. governance: not-required, self: false, harness: false.

CI at this head: settled / green — 49 check runs, 28 of 43 repo-authored workflows inspected the
head, 46 success and 3 skipped. I re-ran nothing locally.

Per-criterion

  1. /pano at 390px lays out at 390px, signed out and signed in — [PASS]. The overflow came from
    .kp-subnav being a nowrap flex row whose zones size to their content. Subnav.css:156-161 now
    sets flex-wrap: wrap on the bar and Subnav.css:164-169 on the nested .kp-subnav__filters,
    with height: auto over a min-height: var(--subnav-h) floor, so the row's min-content width no
    longer drives the document's scroll width. Nothing in the bar or its ancestors clips with
    overflow (AppShell.css sets none). The signed-in half is graded on the mechanism rather than a
    render, because the wrap rules do not vary by session, and the builder discloses that the desk had
    no session (§DEV scope narrowing). The signed-in capture stays owed to review-ui.

  2. Five destinations plus the CTA reachable at 390px — [PASS]. Wrapping is what keeps them
    reachable; Subnav.css:170-173 adds min-width: 0 and tightens padding-inline on each filter,
    and no rule hides or truncates one. .kp-subnav__spacer goes display: none and
    .kp-subnav__cta takes margin-left: auto (Subnav.css:177-182), which keeps the CTA on the
    trailing edge of whichever line it lands on instead of left-jamming against the signal.

  3. Reflow in Subnav.css's existing @media (max-width: 640px) block, following the Topbar.css
    idiom — [PASS]. I read Topbar.css:281-288 at this head: height: auto, min-height: var(--topbar-h), flex-wrap: wrap, gap: var(--s-1), padding: var(--s-1) var(--s-2). The
    subnav block is the same five declarations. Subnav.test.tsx:76-79 asserts the file declares
    exactly one @media, so a second breakpoint reds the suite.

  4. Sticky offset correct when the bar reflows past one line — [PASS]. .kp-subnav's top is
    var(--kp-topbar-measured-h) (Subnav.css:16), the topbar's own border-box height, so the two
    cannot overlap whatever either one wraps to. The narrow block touches neither position nor
    top.

  5. Desktop unchanged at 641px and above — [PASS]. The whole reflow sits inside the max-width: 640px
    block. The one base-rule change is top, and at desktop the measured topbar height equals the
    token it replaced: the PR body's desk reads 38px at both 641px and 1280px, and the bar's own
    height still reads 32px.

  6. Every other SubnavShell consumer still lays out at 390px — [PASS] on the diff. Only
    Subnav.tsx:58 renders .kp-subnav, so sözlük, divan, mecmua, pano and ProductSubnavLayout
    all inherit the same fix. I checked the one consumer whose destinations zone is not the filter
    strip: .kp-sozluk-alphabet is already flex-wrap: wrap (Sozluk.css:81-89), and its in-bar
    override pins height: 100% (Sozluk.css:124-129), which under the narrow block's now-auto
    parent resolves to auto instead of spilling past a 32px bar. That matches the §DEV entry calling
    the sözlük change an improvement. Per-consumer visual confirmation is review-ui's.

  7. review-ui render --viewport mobile captures /pano:auth and /pano:auth-caylak — routed. This
    criterion belongs to the review-ui namespace and needs this PR's preview; I neither graded nor
    discharged it. The §DEV entry records ui render exiting 11 locally on Cloudflare auth, which is
    UNKNOWN rather than a claimed render, and that is the honest disposition.

  8. A unit test asserts the narrow-viewport rule in the stylesheet source — [PASS].
    Subnav.test.tsx:73-110. The narrowBlock helper (Subnav.test.tsx:18-28) brace-matches the
    media block off the source and every assertion runs inside it. That is what makes the negative
    assertions mean anything: a regex over the whole file would pass on a position: static that had
    escaped the query.

  9. .kp-subnav keeps position: sticky at every width, 320px included; no position: static
    [PASS]. Subnav.css:12 is the only position declaration in the file, and
    Subnav.test.tsx:100-101 asserts the narrow block matches neither position: static nor a
    .kp-subnav rule with a top of its own.

  10. The offset is driven by the topbar's measured height, with no hard-coded second-row pixel guess
    — [PASS]. stickyChrome.ts:44-58 measures the bar's border box with getBoundingClientRect and
    writes it to the document root. The two property names are a closed as const map keyed by a
    StickyChromeBar union, so a caller cannot publish a height nothing reads. The only literal
    pixel value anywhere in the change is --kp-subnav-measured-h: 0px (stickyChrome.css:7-10),
    which is the absence of a subnav rather than a guess at one; the topbar default beside it is the
    var(--topbar-h) one-row token. I confirmed --topbar-h is declared on :root and re-declared
    by [data-density] on the document root (tokens.css:497, :561, :588, with
    lib/density.tsx setting the attribute on documentElement), so the :root default resolves at
    every density rather than going invalid.

  11. At 640px and below the subnav sits flush under the wrapped topbar, no overlap and no gap —
    [PASS] on the PR body's desk, which reads top equal to the measured topbar height and a gap of
    exactly 0px at 320px, 390px, 641px and 1280px. getBoundingClientRect over offsetHeight is
    what makes that zero hold: the topbar measures 105.796875px at 320px, and rounding it to a whole
    pixel would leave a hairline of topbar background showing between the bars.

  12. The offset stays correct after the topbar's height changes, rather than being read once at load
    — [PASS]. stickyChrome.ts:57-58 observes the bar with a ResizeObserver and re-publishes on
    every box change, which covers the 640px wrap in both directions and a changed root font size
    without enumerating either. stickyChrome.test.tsx:80-91 drives that path against an observer
    that actually fires, and :93-96 pins that the observed element is the bar itself, the one the
    sticky rule targets. I checked that the inert ResizeObserver shim is test setup only
    (apps/web/tests/client/setup.ts:23), so the shipped path always gets a real observer.
    Full-page browser zoom does not change the bar's CSS-pixel box, so nothing is owed there.

  13. scroll-padding-top set from the same measured offset — [PASS]. stickyChrome.css:17-19 puts
    calc(var(--kp-topbar-measured-h) + var(--kp-subnav-measured-h)) on html. html is the right
    element: .kp-shell is min-height: 100vh in a column flex with nothing scrollable between it
    and the viewport (AppShell.css:5-10), so the document is the scrolling box. No other rule in
    apps/web or packages/design sets scroll-padding or scroll-margin, so there is nothing to
    conflict with.

  14. Keyboard focus never hidden under the sticky subnav (WCAG 2.4.11) — [PASS] on the evidence the
    criterion names. The marker points at two sources. The review-ui captures are the routed gate's
    and do not exist at this head. The PR body's hand-verification section does exist, and I graded
    on it: driving real Chromium over the app's own vite dev server, a backward tab stop on
    origin/main came to rest at top: 46.8px and then -0.2px against a bar bottom at 70px, so
    the failure is pre-existing and reaches desktop too. With this change the lowest backward tab
    stop is 178.8px at 390px against a bar bottom of 141px. The in-page anchor lands at
    140.8px against that same 141px, which is 0.2px of sub-pixel rounding rather than a hidden
    target, and that fractional measurement is exactly why getBoundingClientRect was chosen over
    offsetHeight. The desk's two synthetic parts are named in the body: an empty feed, and an
    appended block of focusable rows so the document scrolls. The bars, their stylesheets and the
    measurement are the real ones.

  15. /pano at 320px reflows with no horizontal scroll and the subnav still sticky — [PASS]. The same
    max-width: 640px block covers 320px, and the desk's 320px row reads 320px document width, no
    horizontal scroll, position: sticky, top 105.797px against a 105.8px wrapped topbar.

  16. The sideways-scroll reflow already landed stays — [PASS]. The wrap rules, height: auto,
    min-height, gap and padding are all still in the block; this round removed only the
    position: static the founder ruled against, and Subnav.test.tsx:81-89 holds the wrap half.

Standing checks

Test honesty. One pre-existing assertion was replaced rather than deleted: Subnav.test.tsx's
rests the bar in normal flow became its opposite. That is disclosed as §DEV class 6, and it is the
right call, because the old assertion pinned the behaviour the founder ruled against, so leaving it
would have red the suite against the ruling. The replacement is a source-text assertion, which reads
thin in the abstract, but criterion 8 asks for exactly that shape and the file's existing rules are
held the same way. The behavioural half is not left untested: stickyChrome.test.tsx drives the hook
against a firing observer for mount, per-bar property, re-measure and unmount.

Comment discipline. The comments are long, and I looked hard at whether they are narration. They
are not. Each states something the code cannot: why the measured height and not the token, why an
auto margin replaces the flex: 1 spacer on a wrapped row, why getBoundingClientRect over
offsetHeight, why readonly callback is a plain field rather than a constructor parameter property
under erasableSyntaxOnly, why the test asserts inside a brace-matched block. No finding.

Staleness traps. The measured height is cached in a CSS custom property, which is the shape this
check exists to catch. It re-validates: the ResizeObserver republishes on every box change, and the
cleanup removes the property on unmount, so a page without the bar falls back to the stylesheet
default rather than carrying a stale number.

Release containment. No new surface. This is a layout fix to existing chrome, so there is nothing
to gate.

Silent failure, type design, test gaps. useStickyChromeHeight returns early when the ref is
unattached, which would silently leave the default standing; both call sites attach unconditionally
to the bar's root element, so that arm is unreachable here. The closed property map makes an unknown
bar name a type error. No test covers two bars of the same kind mounted at once clobbering one
property, which cannot happen in this tree: one .kp-topbar, one .kp-subnav, and Subnav.tsx:58
is the only place the latter is rendered. None of this traces to the issue's stated goal, so I
appended no criterion and filed no follow-up.

Portability guard. Not owed: the diff touches neither claude-plugins/fabrika/ nor
packages/fabrika-cli/src/.

Deviations

# class substance matches what I found
1 Known defect left unfixed no ui render captures; the verb exits 11 on Cloudflare auth locally yes. Exit 11 is UNKNOWN, and recording it as a render would claim a look nobody took
2 Scope narrowing signed-out bar only; no session on the desk yes. Criteria 1 and 2 rest on session-independent wrap rules, and the signed-in render is the design gate's
3 Out-of-scope change added stickyChrome.ts and stickyChrome.css, refs in Topbar.tsx and Subnav.tsx yes. The ruling needs a measured height, no stylesheet can read one, and the reflow itself is still entirely in Subnav.css
4 Out-of-scope change sözlük's alphabet strip is now contained by the growing bar yes. height: auto cannot be scoped to one consumer, and criterion 6 asks for the shared bar to be checked on the shared bar
5 Known defect left unfixed scroll-padding-top also fixes a pre-existing desktop WCAG 2.4.11 failure yes. Scoping the scroll padding to phones would have left the desktop defect standing for nothing
6 Declined guidance dropped the spacer and added margin-left: auto, where Topbar only drops its spacer yes. The Subnav has a trailing-edge primary action (#2587) and the Topbar does not; held by a unit test
7 Pre-existing test changed replaced the rests the bar in normal flow assertion yes. Covered under test honesty above
8 Guard or gate bypassed Subnav.css and stickyChrome.css land in build check's unvalidated yes. No second surface validates a .css file past design-token-guard and design-inventory, and CI is green at this head

Every entry matches something I found in the diff or the body, and I found nothing undisclosed.
deviation-disclosure: PASS, meaning nothing undisclosed that this gate could see.

Terminal

review-code: PASS at 41ae96f5. CI is green, every code-class criterion is discharged or routed,
and the deviations section is complete. The rendered captures of /pano:auth and /pano:auth-caylak
and the per-consumer visual check stay owed to review-ui at the preview.

Evidence for the marked criterion

One criterion on #7730 carries an outside-diff evidence marker, and here is what it rested on.

Criterion: "Keyboard focus is never hidden under the sticky subnav: tabbing down the pano feed at
390px leaves each focused element fully visible below the bar, and following an in-page anchor lands
its target fully visible below the bar (WCAG 2.4.11)."

Evidence named: "the rendered narrow-viewport captures in this PR's review-ui run, plus the
hand-verification section of the PR body".

What I read. The rendered narrow-viewport captures do not exist at this head, and they are the
routed review-ui namespace's to take against this PR's preview, so they are not a source this gate
can reach. The hand-verification section of the PR body I read in full, and it is a measurement
rather than a claim: real Chromium over the app's own vite dev server at 320px, 390px, 641px and
1280px, with the numbers read back off the rendered page. On origin/main a backward tab stop came
to rest at top: 46.8px and then -0.2px against a bar bottom of 70px, and an in-page anchor
landed at -0.2px, so the failure is pre-existing and reaches the desktop. With this change the
lowest backward tab stop is 178.8px at 390px against a bar bottom of 141px, and the anchor
target lands at 140.8px against that same 141px. The 0.2px is sub-pixel rounding of a fractional
measurement, not a hidden target, and it is why the hook reads getBoundingClientRect rather than
offsetHeight. That evidence is what carries the criterion in this namespace; the capture half
stays owed to review-ui.

Verdict-written: 2026-09-21T03:16:41Z

@usirin

usirin commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

review-ui: CANT-SEE at 41ae96f5f8bc7cd47c5f951e67dff8171c8ea9a6

No review-ui verdict is posted. This round the preview did render and I judged what it
served, so this is not the same park as the one at ac023ad2 — it is narrower, and the note says
exactly what is still owed and who can take it.

LAW-SOURCE: manifest-prose (this repo declares no design-prohibitions.json).

What rendered, and what it proves

surface viewport width read back sha256 (first 12)
/pano (signed out) mobile 390×1541 8312bc94f738
/pano (signed out) desktop 1280×1320 a1ebd7875d8e
/sozluk (signed out) mobile 390×844 49210d62c095
/divan (signed out) mobile 390×844 94d3d3d86ef7

Zero page errors on the three that painted a page. Every capture is a full-page shot whose own
PNG header reads 390 wide, which is the document's scroll width — so the sideways scroll the issue
opened on (500px in a 390px window) is gone for the signed-out feed.

/sozluk at 390 is the strongest thing in this set, and it is worth naming because it is not the
surface anyone asked for. Its alphabet strip wraps to three lines inside a bar that grew to
contain it, and + yeni tanım sits alone on a fourth line pinned to the trailing edge. That is
the wrap plus the margin-left: auto CTA rule working on the shared bar, under a wrapped topbar,
at the phone width — 24-plus items, a harder case than pano's six.

/pano at 1280 is a single-row bar, same zone order, 20 başlık at the trailing edge. Nothing in
the desktop shot reads changed.

Advisory, not blocking, and not introduced here: the wrapped alphabet strip's rows sit at roughly a
24px vertical pitch, under the manifest's 36px tap-target floor (pillar 4, whose own note already
routes the concrete fixes to #2166). The letters were under the floor before this change too; the
wrap makes the rows denser rather than the targets smaller. Recording it, not blocking on it.

What did not render, in two kinds

Kind one — the signed-in bar. /pano:auth and /pano:auth-caylak refused on 11 at both
viewports: the preview answered the seeded cookie as a visitor. I narrowed that further than the
standing gap records do. I provisioned both tiers on this PR's own preview D1 through the
sanctioned path (preview-seed test-account, idempotent, refuses any database Cloudflare does not
name as a per-PR preview) — it returned ok, @onizleme-mod at yazar and @onizleme-caylak at
çaylak, sessions valid to 2026-09-28 — and re-ran the render. Still a visitor. So the account half
is done, and the one missing input is the signing secret: the ambient BETTER_AUTH_SECRET here
is 64 chars and not the insecure_ placeholder, but it is not the value the preview worker
verifies with, and the ci-credentials export needs an $ALCHEMY_PASSWORD this shell does not hold.

So the five signed-in destinations plus PanoSubnavCta at 390 — criterion 2, the signed-in half of
criterion 1, and criterion 7 verbatim — stay UNKNOWN. That row is the whole reason the issue
exists, and the issue's own triage note warned against settling it by reading the CSS instead of
loading the page. I will not repeat that error in the other direction and call it a PASS because
the mechanism looks right.

/divan at 390 painted its yazar-gated error screen rather than a Subnav, so divan's half of
criterion 6 is UNKNOWN too. Sözlük's half is PASS.

Kind two — the behavioural half, which no capture can answer at any tier. This is the part I
want on the record, because closing the auth gap would not fix it. Eight of this issue's sixteen
criteria are about what the bar does, not how it composes:

  • stays pinned, flush under the wrapped topbar, while scrolling (criteria 4, 9, and the
    no-gap/no-overlap row)
  • the offset re-measures after a resize across 640px in both directions, browser zoom, and a
    root font-size change (criterion 12)
  • keyboard focus and in-page anchor targets clear the bar after tabbing (criterion 14, WCAG 2.4.11)
  • 320px reflow (criterion 15)

review-ui render takes still captures at scroll 0, and its --viewport set is closed to 390 and
1280. It cannot scroll, resize, zoom, or tab, and it cannot shoot 320px at all. This session's tool
surface carries no claude-in-chrome, so there is no second pair of eyes here either. The PR's
hand-verification table answers all of it and answers it well, but that table is builder-authored
evidence this gate does not consume as its own.

The exact commands owed

One for the gate, once someone hands this shell the deployed secret. The accounts are already
seeded, so nothing else is needed:

node packages/fabrika-cli/src/bin.ts review-ui render --pr 9586 --out auth \
  --surface /pano:auth --surface /pano:auth-caylak \
  --viewport mobile --viewport desktop \
  --auth-secret-from <a file holding the BETTER_AUTH_SECRET the preview worker deploys with,
                      exported from the ci-credentials stack's alchemy state behind $ALCHEMY_PASSWORD>

And one thing that is not a command, because no verb takes it: a human at
https://phoenix-phoenix-pr-9586-nj7i4vzuxm5szohj.kampusinfra.workers.dev/pano, signed in, at a
390px and a 320px window — scroll the feed and watch the bar stay flush under the wrapped topbar,
drag the window across 640px in both directions, zoom, then shift-tab back up the feed and confirm
nothing comes to rest under the bar.

Why this is not a PASS and not a FAIL

Nothing I rendered is broken — no crashed surface, no undisclosed hole (the PR's Deviations
disclose the scope narrowing), no blocking law row tripped, and CI is green at this head including
the design-token seam and the a11y gate. So there is no FAIL ground, and I will not mint one out of
an absence.

But an unseen input blocks PASS, and here the unseen input is the composition the issue was filed
about plus every behaviour the founder's ruling added. Grounding a PASS on the signed-out shots
would be inference dressed as a render: correct-looking CSS reasoning is exactly what said this bar
was fine before someone measured 500px. The gate that cannot see does not get to emit a plausible
verdict, so it emits none.

To the question put to me directly: no, it is not enough. Not because the evidence is weak — the
sözlük capture is real proof the wrap works on this bar — but because the two things this round
turns on are both outside it. One is a surface I cannot authenticate to. The other is a class of
behaviour this gate has no instrument for, at any tier, on any PR. The first is a credential away.
The second is a gap in the gate itself and I am filing it as one.

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.

/pano overflows the mobile viewport: 500px of document in a 390px window

1 participant