Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# CHANGELOG

## 2026-10-22 — Verified pedestrian route exceptions (local candidate, issue #59)

- Keep the existing Progreso International Bridge (230901) and Santa Teresa
(240801) walk-or-drive pages and crossing-detail links when pedestrian reports
disappear. Capability is source-backed; current waits/closures still use the
unchanged report checks. No missing wait becomes zero or a recommendation.
- Add recorded-snapshot generator regression, exact-identity/zero/closure checks
and EN/ES keyboard/navigation/recovery coverage. Official sources, input
identities, evidence and remaining publication gates are in the refresh runbook.
- Local review candidate only. Other ports retain their existing report-based
behavior; #59 and the separate Search Console reconciliation remain open.

## 2026-10-22 — Summary confidence released (PR #143)

- Published source `a14654f32e0b83b11008a978d1fb085ff83db059` through existing
Expand Down
10 changes: 10 additions & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# DECISIONS

## 2026-10-22 · Verified capability and current lane reports answer different questions

The existence of a pedestrian comparison page must survive a missing current
report at a verified pedestrian crossing. Preserve Progreso International Bridge
230901 and Santa Teresa 240801 by exact port identity, backed by the official
sources in the refresh runbook. Do not infer Donna's capability from the Progreso
name, copy another crossing's report, or manufacture zero waits. Availability,
closures and freshness still gate recommendations. Keep other ports' existing
behavior until separately sourced; this two-port exception is not full curation.

## 2026-10-22 · Deferred content must evaluate confidence when it actually opens

A usable snapshot can expire while an optional chunk is downloading, while a
Expand Down
4 changes: 4 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ cross-border experience; it is a product hypothesis, not a claim of demand.
objects in each snapshot, so the inventory fluctuates as reports change.
Keep this separate from document-language ownership; do not invent capability
or expand routes without source evidence.
Issue #59 has a local, source-backed candidate preserving existing Progreso
International Bridge and Santa Teresa routes across missing reports.
Review/publication and other-port curation remain open; see the refresh runbook
for exact input identities, coverage and gates.

- [x] Separate CBP lane report times from snapshot retrieval on dashboard and
crossing detail cards. Preserve raw report text and unknown dates; release
Expand Down
57 changes: 57 additions & 0 deletions docs/REFRESH-OPERATIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,62 @@
# Public data refresh

## Walk-or-drive inventory candidate — 2026-10-22 UTC (issue #59, unreleased)

- Review base: `a10706b5eb5ae48468e32c5fc21c657d63bd8153`, rebased cleanly
from initial base `42ca1fee`; incoming changes were collector inputs only.
Inspected current main,
existing worktrees, the updated issue and runbook before editing; no overlapping
open capability PR found. Preserve original 18 changed paths, prior worktrees
and newer collector inputs. The initial local stage performed no publication.
Review continuation authorizes one branch push and PR/CI, but no merge/deploy.
- Recorded `public/data/crossings.json` inputs, both 42 canonical crossings:
`e0bac72fd4d978dfbbec7f3c68d804025d3ac45c`, retrieved 03:52:35.415Z,
blob `3f6ea59a17f8edc620605c93fa3c7335cfc119e9`, versus
`5a681bad045eb4153830928021350844069cef98`, retrieved 04:52:36.981Z.
Real sitemap/prerender generators agree within each input, but pedestrian
canonicals drop **28 → 26**. Only `/walk-or-drive/progreso-progreso-international-bridge/`
and `/walk-or-drive/santa-teresa-santa-teresa-port-of-entry/` disappear when
their lane objects become null. Collector mapping of absent/N/A/closed reports
to null is not evidence of absent pedestrian infrastructure.
- Source review October 22 supports exactly two persistent positive exceptions,
by port number, without asserting current opening or changing waits:
**Progreso 230901**, [TxDOT 2021 guide, RGV-60/PDF194 and RGV-64/PDF198](https://ftp.txdot.gov/pub/txdot/move-texas-freight/studies/texas-mexico-border-crossings-guide-2021.pdf)
describes pedestrian infrastructure and northbound pedestrian processing;
the [bridge operator](https://texasmexicobridges.com/) also describes pedestrian
passage. **Santa Teresa 240801**, [City of El Paso, International Bridges,
Santa Teresa section](https://www.elpasotexas.gov/international-bridges),
explicitly includes pedestrians. These sources do not establish Donna 230902
or neighboring ports' capability. No source hours are encoded as current status.
- Exact identity cross-check: [official CBP public feed](https://bwt.cbp.gov/api/bwtpublicmod)
returned HTTP200 at 20:13:25.210Z: 230901 = Progreso International Bridge,
230902 = Donna International Bridge, 240801 = Santa Teresa Port of Entry.
These rows corroborate identity only; the dated capability sources do not
establish current operating hours, open lanes or wait availability.
- The shared predicate serves detail links, sitemap, prerender and legacy
aliases. Both recorded inputs now generate **28 canonicals**, identical across
sitemap/prerender. Missing reports still show unavailable; a real zero restores
comparison; closure overrides retained numbers. No wait parser, collector,
freshness threshold or generator implementation changes.
- Broader curation remains open. Six sampled snapshots show other report-based
differences, including Eagle Pass I, Stanton, Pharr, Anzalduas, Lukeville,
Donna, Rio Grande City and San Luis II. No new claim or removal for those ports.
Obtain official port-specific/directional evidence before adding positive or
negative entries. Missing reports alone cannot establish lack of pedestrian
service. #59 stays open; Search Console reconciliation is separately unverified.
- Initial local verification before rebase: fresh build PASS; entry **61.73 KiB**, CSS 19.09 KiB;
full npm test PASS (**133 unit tests, 402 browser navigations**), including both viewports and
EN/ES keyboard entry, canonical identity, missing/deleted reports, true zero,
closure and recovery. Regression runs real generators on recorded inputs in
temporary directories; source snapshots in this worktree are not overwritten.
No budget/audit gate or assertion was removed.
- Publication gates: review the two-port patch; integrate current main while
preserving collector inputs; require fresh build/full suite and exact-head CI
under unchanged **62 KiB**; obtain release authorization for this candidate
(the review/CI continuation still forbids merge/deploy); use the existing workflow once, then verify
manifest/source, both sitemap/prerender pages and EN/ES mobile/desktop links,
missing/zero/closure recovery on served assets. No new dependency, security/
account setting, credential, ad, service, monitor or cost.

## Summary confidence release — 2026-10-22 UTC (PR #143)

- Reviewed head **84da4c8d29bc77940736fc6851962585d4f7e56c**, merged/source
Expand Down
47 changes: 46 additions & 1 deletion scripts/check-walk-drive-availability.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,54 @@ export async function checkWalkDriveAvailability(page, context, baseUrl) {
content = await page.locator('#root').innerText();
assert.ok(!/RIGHT NOW|Right now|Ahora mismo|0 min/.test(content), 'pending wait must not support a recommendation');
}
for (const lang of ['en', 'es']) {
await page.evaluate(value => localStorage.setItem('borderPulse_language', value), lang);
for (const [port, slug] of [
['230901', 'progreso-progreso-international-bridge'],
['240801', 'santa-teresa-santa-teresa-port-of-entry'],
]) {
const c = snapshot.crossings.find(row => row.port_number === port);
assert.ok(c, 'verified capability fixture must exist');
c.port_status = 'Open';
c.lanes = { passenger_standard: lane(60), pedestrian_standard: null, pedestrian_ready: null };
const route = `/walk-or-drive/${slug}/`;
await page.goto(new URL(`/crossing/${slug}/`, baseUrl).href);
const link = page.locator(`#root a[href="${route}"]`);
await link.waitFor({ state: 'visible' });
await link.focus();
await link.press('Enter');
await page.waitForURL(url => url.pathname === route);
const missing = page.getByRole('heading', { level: 1, name: lang === 'en'
? `${c.name}: no current pedestrian wait reported`
: `${c.name}: sin espera peatonal actual reportada`, exact: true });
await missing.waitFor();
await page.waitForFunction(url => document.querySelector('link[rel="canonical"]')?.href === url,
`https://borderpulse.com${route}`);
assert.ok(!/0 min|RIGHT NOW|Ahora mismo/.test(await page.locator('#root').innerText()), 'capability supplies neither a zero nor a verdict');
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1), 'unavailable route fits viewport');
c.lanes.pedestrian_standard = lane(0, 'no delay');
await page.reload();
await page.getByRole('heading', { name: lang === 'en' ? 'Walk (standard)' : 'A pie (estándar)', exact: true }).waitFor();
assert.ok((await page.locator('#root').innerText()).includes('0 min'), 'a genuine zero restores the comparison');
c.port_status = 'Closed';
await page.reload();
await page.getByRole('heading', { level: 1, name: lang === 'en'
? `${c.name}: closed now` : `${c.name}: cerrado ahora`, exact: true }).waitFor();
assert.ok(!(await page.locator('#root').innerText()).includes('0 min'), 'known capability does not override a closure');
c.port_status = 'Open';
delete c.lanes.pedestrian_standard;
delete c.lanes.pedestrian_ready;
await page.reload();
await missing.waitFor();
assert.ok(!(await page.locator('#root').innerText()).includes('0 min'), 'disappearing report removes the zero and comparison');
await page.goBack();
await page.waitForURL(url => url.pathname === `/crossing/${slug}/`);
await link.waitFor({ state: 'visible' });
}
}
} finally {
await context.unroute(feedMatcher, feedHandler);
await page.evaluate(() => localStorage.setItem('borderPulse_language', 'en'));
}
console.log('[walk-drive] PASS: EN/ES closed, Back, reopened zero-wait and pending-report states');
console.log('[walk-drive] PASS: EN/ES closed, Back, zero/pending and verified Progreso/Santa Teresa routes after missing/disappearing reports');
}
119 changes: 119 additions & 0 deletions scripts/fixtures/walk-drive-capability.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
[
{
"sourceCommit": "e0bac72fd4d978dfbbec7f3c68d804025d3ac45c",
"sourceBlob": "3f6ea59a17f8edc620605c93fa3c7335cfc119e9",
"sourceSha256": "8fd1d66e254e9412e37dd7e0b1876f83f1402c4c97218fade2d6332de0c83665",
"fetched_at": "2026-10-22T03:52:35.415Z",
"crossings": [
{
"port_number": "230901",
"name": "Progreso - Progreso International Bridge",
"port_status": "Open",
"lanes": {
"passenger_standard": {
"status": "delay",
"delay_minutes": 10,
"lanes_open": 2,
"update_time": "At 10:00 pm CDT"
},
"passenger_sentri": null,
"passenger_ready": null,
"pedestrian_standard": {
"status": "no delay",
"delay_minutes": 5,
"lanes_open": 1,
"update_time": "At 10:00 pm CDT"
},
"pedestrian_ready": null,
"commercial_standard": null,
"commercial_fast": null
}
},
{
"port_number": "240801",
"name": "Santa Teresa - Santa Teresa Port of Entry",
"port_status": "Open",
"lanes": {
"passenger_standard": {
"status": "delay",
"delay_minutes": 40,
"lanes_open": 1,
"update_time": "At 9:00 pm MDT"
},
"passenger_sentri": null,
"passenger_ready": {
"status": "delay",
"delay_minutes": 35,
"lanes_open": 1,
"update_time": "At 9:00 pm MDT"
},
"pedestrian_standard": {
"status": "no delay",
"delay_minutes": 5,
"lanes_open": 1,
"update_time": "At 9:00 pm MDT"
},
"pedestrian_ready": null,
"commercial_standard": {
"status": "delay",
"delay_minutes": 50,
"lanes_open": 1,
"update_time": "At 9:00 pm MDT"
},
"commercial_fast": {
"status": "delay",
"delay_minutes": 45,
"lanes_open": 1,
"update_time": "At 9:00 pm MDT"
}
}
}
]
},
{
"sourceCommit": "5a681bad045eb4153830928021350844069cef98",
"sourceBlob": "e3eb7c086794d306c8e1b2f6a93fe01667adeaf7",
"sourceSha256": "b7c8e0a77fd42526e5b5868eaf56a2ac85811740e0b0b00e031b11cc1bcdc0ee",
"fetched_at": "2026-10-22T04:52:36.981Z",
"crossings": [
{
"port_number": "230901",
"name": "Progreso - Progreso International Bridge",
"port_status": "Open",
"lanes": {
"passenger_standard": {
"status": "delay",
"delay_minutes": 10,
"lanes_open": 2,
"update_time": "At 11:00 pm CDT"
},
"passenger_sentri": null,
"passenger_ready": {
"status": "no delay",
"delay_minutes": 5,
"lanes_open": 1,
"update_time": "At 11:00 pm CDT"
},
"pedestrian_standard": null,
"pedestrian_ready": null,
"commercial_standard": null,
"commercial_fast": null
}
},
{
"port_number": "240801",
"name": "Santa Teresa - Santa Teresa Port of Entry",
"port_status": "Open",
"lanes": {
"passenger_standard": null,
"passenger_sentri": null,
"passenger_ready": null,
"pedestrian_standard": null,
"pedestrian_ready": null,
"commercial_standard": null,
"commercial_fast": null
}
}
]
}
]
52 changes: 52 additions & 0 deletions scripts/test-crossing-snapshot.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { snapshotHtml, injectSnapshot } from './lib/crossing-snapshot-html.mjs';
import { crossingDisplayName, crossingSearchTerms, crossingSearchTitle } from '../src/lib/crossingNames.js';

Expand Down Expand Up @@ -73,3 +77,51 @@ test('built homepage and every crossing page contain their own useful visible sn
}
assert.doesNotMatch(fs.readFileSync('dist/privacy/index.html', 'utf8'), /data-prerender="crossing-snapshot"/);
});

test('recorded pedestrian-report disappearance preserves the same sitemap and prerender canonicals', () => {
const root = fileURLToPath(new URL('../', import.meta.url));
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'bp-capability-build-'));
const snapshots = JSON.parse(fs.readFileSync(new URL('./fixtures/walk-drive-capability.json', import.meta.url)));
const expected = [
'https://borderpulse.com/walk-or-drive/progreso-progreso-international-bridge/',
'https://borderpulse.com/walk-or-drive/santa-teresa-santa-teresa-port-of-entry/',
];
try {
for (const dir of ['src/lib', 'src/components/utils', 'scripts/lib']) {
fs.cpSync(path.join(root, dir), path.join(temporary, dir), { recursive: true });
}
for (const file of ['scripts/build-sitemap.mjs', 'scripts/prerender.mjs']) {
fs.copyFileSync(path.join(root, file), path.join(temporary, file));
}
fs.symlinkSync(path.join(root, 'node_modules'), path.join(temporary, 'node_modules'));
fs.writeFileSync(path.join(temporary, 'package.json'), '{"type":"module"}');
fs.mkdirSync(path.join(temporary, 'public/data'), { recursive: true });
const unavailable = { ...snapshots[1], crossings: snapshots[1].crossings.map(c => ({ ...c, port_status: 'Closed', lanes: {} })) };
for (const snapshot of [...snapshots, unavailable]) {
fs.rmSync(path.join(temporary, 'dist'), { recursive: true, force: true });
fs.mkdirSync(path.join(temporary, 'dist'));
fs.copyFileSync(path.join(root, 'index.html'), path.join(temporary, 'dist/index.html'));
// Donna is a different port; these two source-backed exceptions must not
// create a pedestrian route there merely because its name says Progreso.
const data = { ...snapshot, crossings: [...snapshot.crossings,
{ port_number: '230902', name: 'Progreso - Donna International Bridge', lanes: {} }] };
fs.writeFileSync(path.join(temporary, 'public/data/crossings.json'), JSON.stringify(data));
for (const script of ['scripts/build-sitemap.mjs', 'scripts/prerender.mjs']) {
execFileSync(process.execPath, [script], { cwd: temporary, stdio: 'pipe' });
}
const sitemap = [...fs.readFileSync(path.join(temporary, 'public/sitemap.xml'), 'utf8')
.matchAll(/<loc>([^<]+\/walk-or-drive\/[^<]+)<\/loc>/g)].map(m => m[1]).sort();
assert.deepEqual(sitemap, expected, snapshot.sourceCommit);
const generated = fs.readdirSync(path.join(temporary, 'dist/walk-or-drive')).flatMap(slug => {
const html = fs.readFileSync(path.join(temporary, 'dist/walk-or-drive', slug, 'index.html'), 'utf8');
if (/http-equiv="refresh"/.test(html)) return [];
const url = `https://borderpulse.com/walk-or-drive/${slug}/`;
assert.ok(html.includes(`<link rel="canonical" href="${url}"`), 'page keeps its own canonical');
return [url];
}).sort();
assert.deepEqual(generated, expected, 'no missing, extra or leftover prerender pages');
}
} finally {
fs.rmSync(temporary, { recursive: true, force: true });
}
});
Loading
Loading