Skip to content

Show what a conference still has to resolve - #2305

Draft
fastfadingviolets wants to merge 13 commits into
codeforboston:mainfrom
hyphacoop:conference-differences
Draft

fastfadingviolets wants to merge 13 commits into
codeforboston:mainfrom
hyphacoop:conference-differences

Conversation

@fastfadingviolets

Copy link
Copy Markdown
Contributor

The conference page gains a "What needs to be resolved?" section, between Committee and Public Input: the House and Senate differences as disclosures with a card per chamber, the points where the texts already match, a one-sentence gist, the reader's own legislator if they sit on the committee, and numbered sources. It is authored in the conference YAML, never generated, and Phone-Free Schools carries lorem ipsum until the real copy is written. The branch is stacked on conference-testimony and targets main, so it should be merged after #2303 is merged

Closes hyphacoop#70

What's in it

🤖 Generated with Claude Code

fastfadingviolets and others added 12 commits October 20, 2026 15:33
/conferences now lands on a hero (what a conference committee is, its five
steps) above the first conference in the rail's order. Every conference
address gets the same hero above it and lands at the conference's top; only
the landing opens on the hero. The explorer moves to /conferences/explorer,
titled Conference Explorer and reached from the hero; a court with no
conferences yet sends /conferences there. The Conferences nav item is
active across all of /conferences.

Scroll snap rests the reader on the whole hero or the conference's top, and
only near the top, since a snapping window eases every scroll further down.
Where the conference starts is measured by a ResizeObserver on what sits
above the hero, as the body is a fixed-height box. Rail clicks land at the
conference's top. Back within the app leaves the reader where the browser
put them, the hero included; a reload lands at the conference's top, since
Layout renders nothing until the client mounts and Chrome has nothing to
restore against. Back from a bill to deep in a conference comes back near
the top: Chrome restores while the shorter bill page is showing, as on
every MAPLE page.

The page has one h1: the hero's title on the landing, the conference's on
an address, visually hidden below md where the picker shows the name. Step
headings sit a level under the hero's title. The hero is built on the
shared PageColumn and reads its steps from the locale.

Closes #81

Verified: jest components/conference 16/16, tsc, eslint and prettier. In
Chrome on dev:local: the landing opens on the hero with its steps, sources
and explorer link; See the committees, a rail click (from 900px into the
landing too) and a direct address land at the conference's top; Back from
a bill to the hero stays at 0; reload mid-conference lands at the
conference's top; the explorer's tab title; an unknown address 404s. In
390px frames: stacked steps, the picker, no horizontal overflow, the h1
present but clipped; --conference-nav matches the header.
Not verified: a screen reader on a real phone; in a browser, the
empty-court redirect, the snap reach after a resize and the reduced-motion
jump; the hero's padding at phone width on PageColumn.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page column now spaces its header and the sections under it with a
flex gap instead of a margin on the header, so a section added below the
committee needs no wrapper of its own to sit 1.5rem from the one above.
The page looks the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The conference page ends with the mockup's Bill Text section: the Senate
and House texts in a recessed well, one sheet each, with a version picker
for Senate, House or both. Both is the default; below md it is the Senate,
as in the mockup. Each sheet scrolls on its own and shows one paragraph per
line the legislature broke.

The texts are fetched in the browser, not with the page: an omnibus pair
is half a megabyte (energy affordability is 533KB), and the page props
would carry it on every view. Both are fetched once by the section, so
switching version shows a text without fetching it again. A spinner holds
each sheet while it loads, and a bill with no stored text gets a sentence
and a link to its PDF.

Text the scraper extracted from a PDF is shown as stored, line numbers and
all; cleaning it up is its own change.

Closes #79

Verified: on the emulator with real scraped bills, data privacy shows
S2619 beside H5479 and community resilience shows S3064 beside H5518's
PDF-extracted text, each sheet scrolling on its own; the picker shows one
sheet and back; in a 390px frame it opens on the Senate with no sideways
scroll; the spinner shows in both sheets before energy affordability's
texts arrive; the gaps between sections are still 24px. Component tests
cover both sheets, one chamber picked with the other still fetched, the
spinner and the PDF link; tsc, eslint and prettier are clean.

Not verified: the PDF link in a browser (no local bill lacks text); a
real phone, only a narrow frame; whether a phone flashes both sheets
before switching to the Senate on hydration; the picker after the
fetch-once change in a browser, only in tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The conference page's title now holds under the navbar with the mockup's
tab bar beneath it: Committee and Bill Text, each tab scrolling to its
section, the section being read underlined. Testimony adds Public Input
between them. The bar was built with testimony's Public Input; it moves
here so the bill text PR carries the page's frame and testimony builds on
it. A bar of one section is left out, as the mockup leaves it out.

A tab lights once its section's top has come up to where a jump parks it,
measured on every scroll and whenever the column the sections sit in
changes size, since a section moves without a scroll when one above it
appears. The observed band it replaces was never reached by the page's
last section, so Bill Text could not light.

The bands pin under the navbar's bottom edge, tracked as the page scrolls,
rather than the stylesheet's 4.5rem. That is shorter or taller than the
desktop bar with the window, and the desktop bar lets go and scrolls away
about a window down, being sticky only inside the layout's one-window box.
On a phone the bar is not sticky, so the bands pin at the top. A tab scrolls
its section to where the navbar will be on arrival: the browser's own jump
takes the margin from before the scroll and lands a navbar's height low once
the bar has gone. The learn page's rail tracks the same edge. A jump also
turns off the hero's snap, which is on near the conference's top and would
snap the landing too, stopping a jump to Bill Text short; the hero turns it
back on near the top.

Verified: on the emulator, data privacy's two tabs jump to their sections
and land 24px under the bar as in the mockup; the lit tab follows the
scroll, and Bill Text lights at the bottom of the page; the sheets still
scroll on their own; the title sits flush under the desktop navbar, follows it up as it
lets go (stretched page: 79, 59, 39px with the bar) and rests at the top
once it has gone, where a jump to Bill Text and back lands 24px under the
tabs; in a 390px frame it pins at the top with no sideways scroll. Under
the hero, See the committees lands on the conference with the title pinned
at the top, and Bill Text, Committee and Bill Text again each land 24px
under the tabs and light (Bill Text stopped 175px short before the snap
was turned off). The bar was laid
beside the mockup at 19bd101. Component tests cover the tabs' targets,
the lit tab following the sections on a scroll and on a resize with no
scroll (failing without the resize), and a bar of one; tsc, eslint and
prettier are clean.

Not verified: a real phone, only a narrow frame; crossing the navbar's
breakpoint by resizing; keyboard focus on the tabs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tab click replaced the history entry's state with null, which is Next's
router state: Back to that entry from another conference rewrote the URL
and left the other conference on screen. The click now keeps the state,
with the hash added to the path Next restores on Back, which would drop
it otherwise. The pinned navbar edge no longer remeasures on scrolls
inside the conference column, such as a bill text's sheet, which cannot
move the navbar; the tabs already ignored them.

Verified: on the emulator, opening Health Worker Safety from the rail,
clicking Bill Text, opening PETS Act from the rail and pressing the
browser's Back shows Health Worker Safety again at #text, the tab click
adding no history entry; jest components/conference 24/24, the new
tab-click test failing without the fix; tsc, eslint and prettier clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Testimony on a committee of conference answers a different question than
testimony on a bill: which version the committee should report, not whether
the bill should pass. Position gains passNothing, passSomething,
passSenateVersion and passHouseVersion. A draft with a conferenceId must take
one of them; every other draft keeps the three bill positions.

It follows ballot questions: one Testimony type scoped by an optional
conferenceId, with counts kept on the conference document. The testimony is
filed against the conference's canonical bill but kept off it: not in its
counts or latest testimony, not in testimony search, and not sent to the
bill's followers. The author's followers still see it.

A user can now hold a bill testimony and a conference testimony on one bill,
so publish, delete, drafts and the version archive match the scope in memory
instead of by query. That avoids stamping conferenceId: null on existing
documents. Excluding conference testimony from search changes the indexed
collection's filter, so each environment rebuilds the testimony collection
on its next deploy. One composite index is added for the conference listing.
Rolling back once conference testimony exists leaves older functions unable
to publish or delete it.

This is the server side only. Nothing under components/ or pages/ changes, so
there is no way to file conference testimony from the site yet.

Verified: functions and site tsc; functions unit tests for testimony and
notifications (32); the testimony, ballot question, moderation, conferences and
count backfill integration suites (59 tests), run on the branch tip where the
functions are unchanged from this commit.

Not verified: that testimony search leaves conference testimony out (no
Typesense locally); publishing on dev.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Conference testimony is filed against the conference's canonical bill, so it
shares that bill's id with the bill's own testimony. The site now keeps the two
apart the way the server does.

The client gets its own ConferencePosition, ConferenceTestimony and conference
draft types, so the bill and ballot-question displays keep their three
positions and none of them change. Listings for a bill, a profile and recent
testimony skip documents with a conferenceId. Draft and publication lookups
match the conference scope with the functions' matchesTestimonyScope, so a conference draft cannot collide with the
bill's draft. useEditConferenceTestimony drafts and publishes on a conference,
and useConferenceTestimony lists what has been filed on one.

Verified: site tsc is clean. The scope match is the functions' matchesTestimonyScope,
whose unit test passes. On the
emulator a conference testimony was drafted, posted and republished through the
real publish function, and its draft carried the conferenceId.

Not verified: a bill page that has testimony, in the browser, since S2581 has no
bill document locally. The listing hooks have no unit test of their own: their
jest suites already fail at the base commit (see the crumb on #69).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ient's mockup

The conference page gets the client's Public Input feed and Add Your Input
panel, built from k-g-k/maple-conference at 19bd101 (the "/" routes, which use
its BUILD flag preset).

The title carries a "Share your input" button, and Public Input joins the
section tabs between Committee and Bill Text. Public Input lists what has been filed as cards,
narrowed by account type and position, with a Following filter that appears
once the reader follows someone, five to a page, and a modal that opens an
entry whole. Each card has the kebab with Follow and Report. The panel writes
on the existing draft and publish hooks: autosaved drafts, a review step, and
posting, which puts the panel away and scrolls to a "Your Input" section.
Editing reopens the panel on the posted text; because the server requires a
reason for every republish, the review step shows one Reason for Edit field
only then, the one control the mockup does not show. The page is keyed by
conference so moving to another starts clean.

Left out on purpose: the mockup's weekly-email checkbox (nothing on the server
backs it) and its Share action (it has no destination). The followers' digest
email and newsfeed card are unchanged and still assume three positions.

Closes #69

Verified: site tsc and eslint are clean. jest passes for components/conference
(18, including a test that moves between conferences and failed before the
page was keyed). The feed card, filter row, and the compose panel and footer
were measured against the running mockup and match, bar the differences below.
Posting, editing, republishing, following (including from the card menu,
which then offers Unfollow), the entry modal and filters were exercised in the browser on the emulator. The server commits below it pass the
testimony, ballot question, moderation, conferences and backfill integration
suites (59).

Not verified: phone width. The review step and the Your Input section were
compared by eye only. The feed's headings and filter row do not pin as the
mockup's do. The mockup labels the reader's new entry "Just now" and animates
the "on the record" line; ours shows the date. The account-type tooltip is a
native title. Avatars are initials. The panel sits under our 72px navbar where
the mockup's is 60px. The report modal and the unverified-email path were
not exercised. The translation files the page loads
have no test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Publishing, and republishing, testimony on a conference that is reportFiled,
enacted or dissolved now fails with "not accepting testimony". Deleting stays
allowed so people can still take their testimony down. Unlike the ballot
question phase, which only the client enforces, this is checked in the publish
transaction too, so calling the function directly cannot bypass it.

On the site, a closed conference keeps its Public Input but offers no way to
write: the title's Share your input button and the Edit item in the author's
own card menu only show while it is appointed. The testimony detail page sent
conference testimony to the bill's form; it now links to the conference page
while the conference is appointed and offers no add or edit after.

Closes #78

Verified: the integration tests added here (publish refused for each closed
status, republish refused after the status flips, delete allowed on a closed
conference) pass. jest covers the closed page (no Share button, Public Input
still there), the card menu without Edit on a closed conference, and the
detail page's conference link and its absence once reported; the menu test
fails with the gate taken out. Site tsc is clean.

Not verified: a reportFiled conference in the browser on the emulator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nce data

yarn emulators:start loads tests/integration/exportedTestData, which predates
conferences: no conferences, no court-194 members or bills, and test@example.com
unverified. Restarting the emulators, or running the integration suite, wipes
whatever was added by hand.

seedLocal reads conferences/194/*.yaml, queues scraper batches so the functions
emulator fetches the real bills and members they name, waits for them, runs
syncConferences, verifies test@example.com, and writes seven sample testimonies
on one conference. Local only.

Verified: on an emulator started clean from the export it filled in 32 bills,
65 members and 16 conferences in about 106 seconds, and every conference page
then showed its six conferees.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ewsfeed

Conference testimony reaches the author's followers, and both places
they see it treated it as bill testimony: the digest email drew a
broken position image and linked the bill, and the newsfeed card
linked the bill and the bill's testimony page.

The digest gives a conference item the conference id and its stance
in words, taken from the site's wording (a test holds the two equal).
Each of the six slots in the user partial sends it through a new
conferenceItem partial that links the conference. The newsfeed card
says the author gave input to the conference committee, shows the
stance chip from the conference page, and links the conference's
Public Input. Bill and ballot-question items are drawn as before.

Verified: functions jest src (123), including buildDigestData and a
render of the user partial for conference, bill and sixth-slot items;
site tsc; NewsfeedCard jest for a conference and a bill card. On the
emulator: the test user followed an author from the conference page,
a new conference and a new bill testimony by that author both reached
the follower's feed, /newsfeed showed both cards with the right links,
and the digest built from the emulator rendered the conference item
with its stance and conference link beside the bill item's image.
Bill rows of the user partial render the same as on main apart from
whitespace between tags.
Not verified: violet has not looked at the email or the newsfeed in a
browser herself, nor has anyone seen the conference stance cell, now
100px wide and wrapping, in an email client. NewsfeedCard's ballot-question test still fails, as
it does on main (the test env has ballotQuestions off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Testimony from before conference testimony has no conferenceId, and
Firestore cannot select on a missing field, so every read that kept bill
testimony apart from conference testimony fetched the whole bill and
filtered in memory. backfillTestimonyScope stamps null on whichever of
ballotQuestionId and conferenceId a published, archived or draft
testimony lacks (the ballot-question backfill skipped drafts, and this
replaces it, in CI too), with --dry-run. Drafts are now written with
conferenceId: null.

Publish, delete, the counts backfill, the website's draft and
publication lookup, the bill's listing and a testimony's version history
then filter both fields in the query, with their limits back: publish,
delete and the counts backfill are main's code plus one filter, and
matchesTestimonyScope goes. The version history fixes a conference
filing listing its author's bill versions on the canonical bill, and the
reverse. Unscoped listings keep the in-memory filter. Two composite
indexes and a conferenceId override are added; the bill listings rely on
index merging, as the ballotQuestionId filter already did. The
integration test data is migrated with the script.

Run per environment, dev then prod: indexes first, then the backfill,
then the functions and hosting, then the backfill again for what the old
code wrote in between. Stamping after the new code is live hides legacy
testimony from its bill and lets a republish duplicate it.

Closes #82

Verified: site tsc; functions jest 120/120; yarn test:integration-ci
tests/integration components/db, 102 pass, including the backfill on
legacy docs and its dry run, republishing backfilled legacy testimony in
place beside a conference filing, and delete skipping a newer conference
filing for the bill's latest (fails without the filter); the script's
dry run, run and second dry run against the exported test data (25
published, 133 archived, 38 drafts, then none left).

Not verified: that the indexes serve every query, which the emulator
does not check, so only dev shows it; the migration on dev or prod data;
the full yarn test:integration-ci, which aborts partway at this commit's
parent too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
maple-dev Ready Ready Preview Oct 21, 2026 8:08pm UTC

Request Review

@fastfadingviolets

Copy link
Copy Markdown
Contributor Author
image

what it looks like (with lorem ipsum copy)

The conference page gains the mockup's "What needs to be resolved?"
section between Committee and Public Input, with an Unresolved Text tab:
a one-sentence gist with a feedback link, the differences as disclosures
with a Senate and a House card each (the first open, "Not in the bill"
where a chamber's text is left out, noted terms as tooltips), the
matching points beside them where there is room, a line naming the
reader's own legislator, and the sources as numbered marks with a list
at the foot of the card. A conference with nothing written has no
section and no tab.

The conference YAML gains a gist, chamber texts that may be left out,
and term notes; the page loader now passes gist, differences and
similarities through. Phone-Free Schools carries lorem ipsum until the
real copy is written. The tab row's height is one constant shared by the
pinned heading and every section's scroll margin, and the feedback form
address moves to components/common.

Closes #70

Verified: jest components/conference 41/41 (in the next container);
tsc and eslint; test:integration-ci tests/integration/conferences.test.ts
11/11. In Chrome beside the mockup at 19bd101, desktop and a 390px
iframe: heading, labels, chamber cards, answer and match text, gist,
term tooltip and the pinned heading match by computed style.

Not verified: the your-legislator line in the browser (jest only, no
signed-in profile); the copy itself, which is lorem ipsum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview – maple-dev — f3bf6d89 Deployed Oct 21, 2026 by vercel[bot]
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.

House vs Senate differences and similarities, authored by journalists

1 participant