Skip to content

Name the first door instead of an empty paragraph, and stop calling the night a fault - #82

Merged
EnesYilmazcode merged 2 commits into
mainfrom
lane/night-gate
Sep 1, 2026
Merged

Name the first door instead of an empty paragraph, and stop calling the night a fault#82
EnesYilmazcode merged 2 commits into
mainfrom
lane/night-gate

Conversation

@EnesYilmazcode

@EnesYilmazcode EnesYilmazcode commented Sep 1, 2026

Copy link
Copy Markdown
Owner

What it looks like

At 11:40pm on a Monday the whole app is one card: heading Nearest buildings,
an empty paragraph, a button reading Show nearest buildings, all inside an
orange border. Every holiday gets a paragraph explaining itself; the ordinary
night, which is most of the year, gets nothing and an alarm colour.

  gate-h  "Nearest buildings"      ->  "Monday, 11:40pm"
  gate-p  ""                       ->  "Classes are done for the day. Vacant ranks
                                        rooms again on Tuesday at 8:00am."
  border  rgb(255, 176, 46)        ->  rgb(29, 35, 44)
  #near   "Everything is closed    ->  "Everything is closed. Independence Hall and
           right now."                  2 more open at 7:00am."

Mechanism

On main, paintGate starts at js/app.js:1766 and borrows UNSCHEDULED at
:1778-1779. That pair belongs to the buildings screen and its body is '' by
design, so the heading repeated its own button over an empty paragraph. #gate
carried --warn in its base rule at index.html:195-196, so the clock wore the
colour a missing fact wears everywhere else, for 6,405 of the 10,080 minutes in a
week.

Three new pure functions in js/state.js: nextOpening walks forward a week for
the first door that has not already opened, openDoorCount says how many are
unlocked right now, and unscheduledGate writes the line. All three are
location-free, because whether campus is shut is not a fact about where the
reader is standing; js/app.js swaps in the nearest of any tied doors on the
screen that does know.

Honesty, which is where the work went

Five ways this screen could state something the app cannot back, all of them new
because main printed an empty paragraph here. Every figure is a sweep of the
shipped data/rooms-1268.json and data/current.json.

The day the list comes back was read off busyDay.weekdays, a weekly Mon-Fri
mask counted from block totals with no calendar in it, so it named days
resolveState already refuses on: 3,780 of the 94,665 gate minutes of Autumn
2026, 3.99%
. 3,105 were one weekend, every gate minute from Friday 20:15 to
Sunday midnight promising rooms on Labor Day; the rest Veterans Day, Thanksgiving
and 2026-12-10. nextScheduled now steps real dates through
scheduleCoversDate, the day half of inScheduledHours (js/state.js:480 on
main) lifted out of it, and drops the clause when it cannot see a day it will
rank on. The test walks the sentence's named day back to a date and asks
resolveState, a different function reading a different thing.

The door clause fired with campus already open behind it, on 3,885 of a
week's 6,405 gate minutes, including 7:30am on a Tuesday with all 46 buildings
unlocked, under a button leading straight to a list of them. The buildings screen
guards the same sentence on groups.open.length; the gate now has the same fact.

data/buildings-hours.json is weekly and hoursFor indexes it by weekday, so
nothing in it knows about a holiday. On Thanksgiving the buildings screen printed
PAES opens at 5:00am under its own heading saying campus is locked, and on the
Sunday before Labor Day it said the same about the Monday. firstDoor drops the
door when the day it lands on is one the university closes.

Three registrar no-classes weekdays are days the mask says yes to and
resolveState already flags. The gate said the opposite on both sides of them,
Classes have not started yet at 3am on Autumn Break and Classes are done for the day at 11pm. It now says what the ranked screen says on the same date.

The heading is a live minute and nothing repainted it. visibilitychange
(js/app.js:2019 on main) covered the list and buildings screens only, and the
gate lives inside #ask.

Evidence

The real app, off a local server, pinned to the Thompson Library steps, with a
clock that can be moved after boot. #gate and #near read back from the DOM:

Mon 2026-09-14 23:40  class=""         rgb(29, 35, 44)  "Classes are done for the day. Vacant
                                                         ranks rooms again on Tuesday at 8:00am."
Tue 2026-09-15 07:30  46 doors open                     "Classes have not started yet. Vacant
                                                         ranks rooms again at 8:00am."
Thu 2026-10-15 03:00  a no-classes day                  "Autumn Break. No classes are meeting
                                                         today. PAES opens at 5:00am and Vacant
                                                         ranks rooms again at 8:00am."
Fri 2026-09-04 20:15                                    "...rooms again on TUESDAY at 8:00am."
                                                         (was Monday, which is Labor Day)
Wed 2026-12-09 20:30  last class of term                "Classes are done for the day."
Mon 2026-09-07 14:00  class="refusal"  rgb(255,176,46)  Labor Day, unchanged

Sun 2026-09-06 23:00  #near .empty  "Everything is closed right now."  (was naming Labor Day's door)
Thu 2026-11-26 03:00  #near .empty  "Everything is closed right now."  under "Thanksgiving Day,
                                                                       campus is closed"
Sat 2026-09-05 03:00  #near .empty  "Everything is closed. Independence Hall and 2 more open
                                     at 7:00am."

The stale heading, same page, clock walked forward with visibilitychange fired
at each step. Before the one-word fix the card was byte identical at all four:

  boot Mon 23:40  "Monday, 11:40pm"    "Classes are done for the day..."
  Tue 00:20       "Tuesday, 12:20am"   "Classes have not started yet. PAES opens at 5:00am..."
  Tue 01:10       "Tuesday, 1:10am"    same
  Tue 10:00       gate hidden, duration chips back

Two numbers did not reproduce and were heading into an append-only
docs/DECISIONS.md. The byDoor sweep is 2,984 of 96,768 (3.08%), maximum row
move 1
, not 4,066 / 4.20% / two places, and the Cockins Hall and Agricultural
Engineering example never happens: the pair never ties on walk and metres at any
of the 144 grid points the sweep uses, and over a 201x201 grid of the same box
there is no origin where both come out 1,003 m. Where they do tie, both read
2,775 m. The key is now the next door rather than opensAt, which for a row
already shut for the day is the minute it opened this morning; that fixed four
cases of a building shut until tomorrow sorting above one still to open. Both
figures are asserted in the test now, so they fail rather than rot. The
nextOpening header said a Saturday night names 46 doors opening Monday;
measured, Saturday publishes 5 doors and Sunday 11, the answer is three opening
7:00am Sunday, and Monday is unreachable from any Saturday minute.

The focus-ring measurement stands as written: on main a heading reached by
mouse comes out outline: none and the same heading reached by Tab comes out
solid 3px; with the option it is none on both routes, and .opt, .chip,
.row, .b-row and .bar-btn all keep their ring.

Tests

Nine tests became twenty under // ---- the night gate; nothing that exists on
main is edited or deleted and git diff main -- scripts/test/screens.test.mjs
still removes no line. The first nine were all on pure functions, so every one of
the seven js/app.js and index.html changes reverted with the suite green,
including the bug in the title. Eighteen mutations now go red, one for every
screen change and every correctness fix, including all seven that used to pass:
UNSCHEDULED back in paintGate, var(--warn) back in the #gate base rule,
either refusal class call deleted, focusVisible: false or the
h2[tabindex="-1"] rule dropped, the buildings sentence reverted, and the
same-day join short-circuited. The source-text assertions use the idiom
scripts/test/dev.test.mjs:50 and the container-query test already have.

617 tests, 604 pass, 4 skip. The 9 alert.test.mjs failures on this box are
a bash artefact and fail identically on main. node scripts/shoot.mjs --check
is clean on both trees, interleaved, and writes nothing.

Uncertainty

nextOpening names whichever tied building the index reaches first, so a rebuild
that reorders rooms could swap the name in a sentence that stays true; the
buildings screen overrides it with the nearest tie, the gate cannot, having no
origin. firstDoor's calendar guard and the nearest-door pick live in js/app.js
and are held by source-text assertions plus the driven runs above, not by a unit
test, because js/app.js needs a DOM the offline suite has not got. The today
day word is unreachable on the shipped table now, so the test drives it from a
door handed in; it stays because a term whose earliest door moved past 8:00am
would need it. scheduleCoversDate adds an exam-window check inScheduledHours
did not have, which changes nothing on the shipped calendar because exam week is
already out of term and already dark. SHELL_CACHE is stamped from this branch's
parent and has to be re-run on the merge commit.

…he night a fault

At 11:40pm on a Monday the whole app was one card reading "Nearest buildings"
over an empty paragraph, above a button reading "Show nearest buildings", inside
an orange border. Three ways of saying nothing, and the orange said something was
broken when the only thing that had happened was the time of day.

The gate now says the minute in its heading and one line under it.

  before                        after
  Nearest buildings             Monday, 11:40pm
  (empty)                       Classes are done for the day. On Tuesday PAES
                                opens at 5:00am and Vacant ranks rooms again at
                                8:00am.
  border --warn                 border --line

nextOpening scans forward up to a week for the first door that has not already
opened, and it is location-free on purpose: three buildings open together at
7:00am on both weekend days, and nothing in state.js knows where the reader is
standing, so it names the first the index reaches and the count carries the other
two. The buildings screen uses the same function, so at 3am it reads "Everything
is closed. Hitchcock Hall and 2 more open at 7:00am." rather than stopping at
"Everything is closed right now." while holding all 46 opening times.

A door opening and a ranked list coming back are different promises. On a
Saturday they are 49 hours apart: the first door is 7:00am that morning, the
first ranked room is 8:00am on Monday, because only 5 of the 46 buildings publish
Saturday hours and busyDay.weekdays[6] is false. So the Saturday sentence names
Monday and never promises rooms at 8am.

Writing the sweep test found a second one. The last weekday door opens at 7:00am,
so between 7:00 and 7:59 the next door is TOMORROW'S, and a bare "and Vacant
ranks rooms again at 8:00am" sitting behind "On Saturday" reads as Saturday when
it means 59 minutes away. 300 minutes of the week, one hour every weekday,
immediately before class. The clause carries "today" in exactly that case now.

Orange means a refusal. paintGate adds .refusal for the six states refusedState
dresses and leaves it off for the clock, which the gate wears for 6,405 of the
10,080 minutes in a week. Labor Day at 2:00pm comes out rgb(255, 176, 46),
Monday at 11:40pm comes out rgb(29, 35, 44), read off the computed border.

The closed group gets opensAt as a last-resort tiebreak under the walk, measured
rather than assumed: over a 12x12 grid on the campus box at every quarter hour of
every day, 4,066 of 96,768 closed lists (4.20%) come out in a different order and
no row moves more than two places. Cockins Hall and Agricultural Engineering are
both 1,003 m from the south of campus, one opening 12:30pm on a Sunday and the
other shut all day, and the shut one used to sort first.

A heading is not a control. focusHeading moves focus to an h2 so the reader lands
on the new screen, and a script focus is scored with the modality of the last real
input, so a heading reached from the keyboard matched :focus-visible and wore the
3px ring meant for buttons. Driven headless on main: the buildings heading reached
by mouse comes out outline none, the same heading reached by Tab comes out outline
solid 3px. focus({ focusVisible: false }) is none on both routes, and the
h2[tabindex="-1"] rule is the same answer for an engine that drops the option.

Nine tests appended, none edited. 606 tests, 602 pass, 4 skip.
…s to it

Review found the night gate saying five things the app cannot back, all of them
new, and no test on any of the seven screen changes.

The gate promised the ranked list back off busyDay.weekdays alone, a weekly
Mon-Fri mask counted off block totals with no calendar in it, so it named days
resolveState already refuses on: 3,780 of the 94,665 gate minutes of Autumn
2026, 3.99%, pointing at Labor Day, Veterans Day, Thanksgiving or the day after
the last class of term. 3,105 of those were one weekend, every gate minute from
Friday 20:15 to Sunday midnight offering rooms on Labor Day. nextScheduled now
steps real dates through scheduleCoversDate, which is the day half of
inScheduledHours lifted out of it, and drops the clause when it cannot see a day
it will rank on. The sweep is held against resolveState, a different function
reading a different thing, so it is not the sentence marking its own homework.

It also named the first door with campus already open behind it, on 3,885 of the
6,405 gate minutes of a week, including 7:30am on a Tuesday with all 46
buildings unlocked under a button leading straight to a list of them. The
buildings screen guards the same sentence on groups.open.length; openDoorCount
gives the gate the same fact without needing an origin.

data/buildings-hours.json is weekly and hoursFor indexes it by weekday, so
nothing in it knows about a holiday. On Thanksgiving the buildings screen printed
"PAES opens at 5:00am" directly under its own heading saying campus is locked,
and on the Sunday before Labor Day it said the same about the Monday. firstDoor
drops the door when the day it lands on is one the university closes, and both
screens go back to "Everything is closed right now."

The registrar publishes three no-classes weekdays that the mask says yes to and
resolveState already flags. The gate said the opposite on both sides of the day:
"Classes have not started yet" at 3am on Autumn Break, "Classes are done for the
day" at 11pm. It now says the same words the ranked screen uses on the date.

The heading is a live minute and nothing repainted it. Booted at 11:40pm on a
Monday and brought back at 10:00am on the Tuesday, the card was byte identical,
still naming Monday night on a minute the app ranks. visibilitychange now covers
the question screen too.

Two numbers did not reproduce and were heading into an append-only record. The
byDoor sweep is 2,984 of 96,768 (3.08%) with a maximum row move of 1, not 4,066
/ 4.20% / two places, and the Cockins Hall and Agricultural Engineering example
never happens: those two never tie anywhere on the grid. The key is now the NEXT
door rather than opensAt, which for a row already shut for the day is the minute
it opened this morning, and fixed four cases of a building shut until tomorrow
sorting above one still to open. The nextOpening header said a Saturday night
names 46 doors opening Monday; the answer is three opening 7:00am Sunday, and
Monday is unreachable from any Saturday minute.

On tests, all nine of the previous ones exercised pure functions, and every one
of the seven js/app.js and index.html changes reverted with the suite green,
including the bug in the branch title. Eighteen mutations now go red, covering
each screen change and each correctness fix; the source-text assertions follow
the idiom dev.test.mjs and the container-query test already use.

Also here: the buildings screen picks the nearest of three tied doors rather than
the first the index reaches, Independence Hall at 120 m over Hitchcock Hall at
460 m; WEEKDAYS renamed to DAY_NAMES, the name js/dev.js already uses, because it
sat one line above busyDay.weekdays meaning something else; comment density on
the added lines cut from 92 to 77 per 100 in js/state.js and 79 to 56 in
js/app.js; and sw.js re-stamped, shell 77,138 bytes and the cache name moved off
the one main carries.

Driven headless in the real app at every instant named above, from the pinned
Thompson Library origin, with a clock that can be moved after boot. This branch's
nine tests became twenty, four of them rewritten for the signatures that moved;
nothing that exists on main is edited or deleted, git diff main on the test file
still removes no line. 617 tests, 604 pass, 4 skip; the 9 alert.test.mjs failures
on this box are a bash artefact that fails identically on main.
node scripts/shoot.mjs --check is clean on both trees, interleaved, and writes
nothing.
@EnesYilmazcode
EnesYilmazcode merged commit 71e27e2 into main Sep 1, 2026
2 checks passed
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.

1 participant