Releases: Skythe7/DiresQ
Releases · Skythe7/DiresQ
Release list
v1.1.0
Added
- The clock can be scaled, so the thing this project is about can be watched instead of described. DiresQ's mechanism is the absence of an event over time — a deadline passes, nobody hears from a responder, and fifteen minutes later the server files a report about them — which is correct and completely unwatchable.
DIRESQ_DEMO_SPEEDmultiplies the rate incident time passes: at 60, a responder goes overdue five seconds after the page loads and the report about them arrives fifteen seconds after that. Nothing about the mechanism is faked for the camera. There is onenow()and every deadline, comparison and query downstream is the production code, unchanged; only the rate time enters the system is scaled. Login lockout and the ICS-214 export filename deliberately stay on the real clock, the first because scaling a security control on a public instance is not a demo feature and the second because it names a file on somebody's disk. A scaled clock forces the demo banner on and makes it state the multiplier, for the same reason the board shows when the sweep last ran: a page showing accelerated time has to say so, or the honest version of this is indistinguishable from a faked one. - The accountability board shows time remaining, not only time elapsed. Every row already said how long since we last heard from somebody, which states the past and leaves the future to be worked out. It now also says due in 5 min, counting down, and 20 min past due once it isn't. The number is in the
/api/responderspayload asdue_in_seconds, negative once the deadline has gone. - A seeded responder whose deadline expires while you are looking at it. Everybody else on the demo board is in a state that has already settled — Sam is red, the Mayde Creek four are overstaffed, the rest have time in hand.
n.farrowis the only one whose state changes on screen, because a transition needs a before, and the first attempt gave them the longest deadline on the board, so every other responder went red first and there was nothing to point a camera at. A test now asserts they lead the queue by at least three minutes, so a later change to the seed cannot quietly take the shot away again. docs/filming.md: the arithmetic that turns a speed into a shot (red at 300/S seconds, the auto-filed report at 1200/S), why speeds above about 120 look broken rather than fast — the board refreshes every three seconds, so the countdown starts jumping by more than it counts down — and a shot list that opens cold on a red board rather than on anybody introducing themselves.- Two buttons on the map for when you have lost the incidents. Fit all frames every report currently shown — including after the coverage filter has hidden some, so it frames what is left rather than what it just hid. Next report walks through them one at a time and opens each. Both say where they went to a screen reader, because moving a map is silent.
- A
CONTRIBUTING.mdand aCODE_OF_CONDUCT.md. The contributing guide is specific rather than generic: a fix arrives with a test that fails without it, numbers in prose have to be true because a test reads them, a weakened guarantee gets a paragraph inlimits.md, and nothing that stops being true when it is written may be cached. The code of conduct is the Contributor Covenant with two additions — one rule about not putting real emergency information into a demo that cannot summon help, and an honest note that two students are not a moderation team, with a pointer to GitHub's own report-abuse flow for anything urgent. - The board now shows when the silence check last ran — "checked 2s ago" beside the live indicator, turning amber if it has not run for five minutes. The check that files a report about somebody who has gone quiet has no scheduler: it runs whenever anybody reads the board, so it cannot be a timer that dies without anyone noticing. But that was a claim in a comment, and this project does not ask anyone to take a claim about an alarm on trust. On a board somebody is watching the number always reads a few seconds, because the watching is what runs it — which is the point. You can watch it move instead of believing it.
app.pyopens with an index of what is in it: fifteen sections in running order, each one a banner comment you can search for. It is one file on purpose, and the cost of that is knowing where anything is. There are no line numbers, because they would be wrong by the next commit, and a test checks the index and the file still agree — including the order.- The pages that describe the silence check now describe the version of it that exists.
limits.mdstill says the switch depends on being run, because it does — but says that the dependency is now visible on the board rather than only in the documentation. A limitation you can see on a screen is a different claim from one you have to be told about, and the difference is worth writing down. - The README now states the total amount of writing in the project — code and documentation together — and a test holds it to the truth, counting only what somebody actually typed. The first measurement was nine and a half thousand lines too high because it counted a package lockfile, a generated model and the documentation site's copy of the documentation; there is now a test that fails if any of those creep back into the total.
Changed
docs/accessibility.md,docs/limits.mdanddocs/decisions.mdcaught up with the demo clock. The accessibility page records the fourth pass and the three things it found, and its enforcement table now lists what the new tests actually check rather than what the old ones did — including the detail that the full-stylesheet contrast audit got its light/dark classification wrong on the first attempt and was caught by its own canary.limits.mdrecords that screen reader users do not get the countdown at all, why the alternative was worse, and that the honest fix is not written.decisions.mdrecords why time is injectable rather than the constants being shortened in demo mode, and what stays on the real clock.- The tests that read the project's own files stopped walking into
node_modulesbefore discarding it. They were the slowest thing in the suite and are now roughly thirty times faster.
Fixed
- Six stylesheets were setting real text in
#6c7086or#45475a, which are 1.4:1 to 1.9:1 against the surfaces they sit on where WCAG asks for 4.5:1 — the disclaimer's footer, the legal links under the login, signup and homepage forms, the offline page's "as of" stamp and footer, the triage footnote, and the stood-down vote and capability tags on a report. The test that was supposed to prevent this checked ten hand-listed colour pairs, so a colour used in a file nobody had thought to add to the list was never looked at. It now reads everycolor:declaration in every stylesheet, resolvesvar()against each file's own palette, and checks it against the backgrounds it can actually sit on. - The countdown on the accountability board survived exactly one render. It was added to the server-rendered template and not to the JavaScript that replaces the list every three seconds, so it appeared on load and was wiped by the first poll — invisible in the test suite, which reads the template, and invisible by hand unless you happened to watch the same row for three seconds. A test now asserts both renderers draw it. The board's coordinates and its "not assigned" / "no position" / "no contact yet" states were using
--overlayon the row background, which is 1.92:1 against WCAG's 4.5:1. That grey has been banned froma11y.cssby a test since it was caught on capability tags;board.csswas never covered by it and now is. - The hosted demo stopped booting. The boot script asked for two gunicorn workers on a 512 MB instance, and the classifier trains when the app is imported, so each worker carried its own copy. Render prints the worker count it sized the box for — one — in its own deploy log, and passing
--workers 2silently overrode it. The container was killed four seconds after starting, and the platform then spent seventeen minutes health-checking a process that was already dead, which is why the failure reported itself as a health check that never passed rather than as a boot that never survived. The count now comes from the host. - Responders were drawn on the map in a way the legend no longer described. The legend key had been changed to a hollow ring — colour was already saying whether anyone is coming to a place, so it had nothing left to say about a person — but the map was still drawing them as solid circles in those same three colours. So the symbol people were told to look for did not exist, and the ones that did exist looked like reports. Fixing a label without fixing the thing it labels leaves a page telling two lies instead of one.
- The board, the map and the feed could return an error page under load. All three run the silence check before answering, the check writes to the database, and SQLite lets one writer lock out every reader — so with a few people watching a board that refreshes every three seconds, a write could wait past its timeout and fail the whole response. The pages that exist to be watched during an emergency were the ones that broke when watched. Three changes: the database now uses a journal mode where readers and the writer stop blocking each other, the check runs at most once every thirty seconds instead of once per visitor per refresh, and a failed check is caught rather than allowed to become an error page. It deliberately does not record itself as having run when it fails, because a board showing green while nothing is checking is the worst outcome available.
- A report could say somebody was on scene while the board had them forty-five minutes overdue, and the report page gave no sign of it. Status is what a responder last told us; whether they are still answering i...
v1.0.2
Added
- Every tagged release now carries a launcher for macOS and Linux and one for Windows. Download the one file, run it, and it fetches the source for that version, builds an isolated environment, seeds the demo database and opens the browser. Everything lands in a folder beside the script, so deleting the folder leaves nothing behind. Published with a
SHA256SUMS.txtso a download can be checked before it is run.
v1.0.1
Fixed
- The pin that marks the spot you picked on a new report showed as a broken image. It was Leaflet's own marker graphic, served from the same CDN as its stylesheet, and the app's image policy does not allow that host. The pin is now drawn in CSS like the ones on the map, so there is nothing left to fetch and nothing left to block.
- Filing a report with no signal could not set a location. The map library loads from a CDN, so with no network it was not there, and the script that fills in the coordinates stopped on its first line — leaving nothing that could put a position into the form, and the server rightly refused a report without one. The picker now expects the map to be missing, keeps working without it, and is stored on the device with the rest of the offline pages. Your position still comes from GPS, which is a sensor and does not need a network.
- Pressing Use My Location and being refused, or waiting past a timeout, did nothing visible. It now says which happened, and gives the location a longer window to arrive, because a cold GPS fix outdoors is not instant.
- The coordinates you picked are now announced to a screen reader rather than only shown.
- The map went grey a moment after it loaded, and stayed grey as you moved around it. The service worker was forbidden from fetching map tiles by our own security policy: a worker is governed by the headers on its own script, and everything it fetches is judged as a network connection rather than as an image, so the rule that lets the page draw tiles did not apply to it. The worker now carries its own policy, narrower than the page's, that permits exactly the tiles and nothing else. Until the worker took over the page fetched its own tiles, which is why it always looked fine for the first second.
- Map tiles were never actually being kept. Tiles come back in a form the app is not allowed to read — deliberately, since we never asked for permission to read them — and the check for a good response treated every one of them as a failure. The map worked anyway with a connection, so an empty cache looked exactly like a full one, right up until somebody lost signal. Tiles you have already seen are now stored, which is what this was always documented to do.
- Both spellings of the OpenStreetMap tile address are now permitted, so the map will not break on the day they finish retiring the older one.
- A report could be filed at a place that does not exist. Nothing checked that a latitude was a latitude, so a coordinate off the globe was stored, counted in the totals, listed in the feed, and drawn nowhere — the report was real everywhere except the map. Locations outside the world are now refused, and the poles and the antimeridian, which are real places, still work.
- Getting anything wrong on the report form threw away everything you had typed. Somebody who wrote out a situation and forgot to set the location got the blank form back and had to write it again. The form now comes back with your words, your priority, and your pin still in it.
- On the report form, four fields had labels that were not attached to them, so a screen reader announced an unnamed text box and left the placeholder as the only clue — and placeholders vanish the moment you type. The title above the location picker was a label pointing at nothing at all, which is its own kind of lie. The breaths-per-minute question in triage had no name either.
- The small print explaining how each form works was the same colour as the card behind it — 1.4:1, where 4.5:1 is the readable minimum. It was the text that tells a first-time user what to do, including the normal breathing range the triage question cannot be answered without.
v1.0.0
Added
- File a report from your phone: what is happening, how bad it is, and where. The location comes from tapping a map or from your device's GPS, so it works when you cannot name the street you are standing on.
- A feed of every open report, worst first. Each card shows how many people are already on their way and how many have arrived, so a report nobody has touched is visible as such.
- Search and filter the feed by severity.
- Open any report to see everything known about it, including every responder currently assigned to it rather than a single named rescuer.
- Join a report. Any number of people can join the same one. There is no claim lock, because in a real disaster the failure is convergence, not collision.
- Move yourself through en route, on scene, and cleared as you go, so the board reflects where people actually are. Buttons on the report page drive all of it: respond with an ETA, mark yourself on scene, signal staffing, check in, and resolve. Plain forms, so they work with JavaScript off.
- The report page lists everyone on it with their status and staffing vote, and only offers you the actions you are actually allowed to take.
- A real not-found page instead of an empty report.
- Once on scene, tell everyone else how staffed it is: needs more help, adequate, overstaffed, or stood down. Only people physically there can set this, because only they can see it.
- The accountability board at
/board: every responder, what they are doing, where they were last seen, and how long since anyone heard from them. Anyone overdue sorts to the top and their row turns red. The page refreshes itself every three seconds, so someone going overdue appears without anyone touching the screen. It renders server-side first, so it still works with JavaScript switched off. - Check in to reset your timer and update your position. A check-in can say when it was really made, so one queued while offline is judged on when it happened rather than when it synced. The board shows both times and marks the ones that arrived late.
- The feed refreshes itself, so a card reorders when someone two streets away changes their staffing signal.
- Flag a report as fake. One flag each; at three it drops out of the feed but stays visible to whoever filed it and anyone already on their way.
- Marking yourself on scene when your last check-in was over 500 m from the report raises a position mismatch on the board. It catches honest errors and lazy faking, not someone determined to lie.
- A triage helper for when you cannot judge how bad something is. Four questions anyone can answer without training or equipment, run through START, the protocol used at real multiple-casualty scenes. It returns a category, the severity that files the report as, and a plain-English reason. The severity dropdown still works; not every report is a casualty.
- When a responder stays silent fifteen minutes past their deadline, the server stops waiting to be noticed and files a report itself, at their last known position, marked as automatic. It behaves like any other report: it can be joined and resolved. Only ever one per person while it is open.
- A banner at the top of the feed counting the reports nobody is going to. Distinct from understaffed: it counts only the ones where nobody has said they are coming at all.
- Check-ins can arrive as bytes instead of as a browser. A check-in packs into twenty-two bytes, which fits a LoRa payload with room to spare, and
POST /api/uplinkaccepts one from something that has no session to log in with. Both routes in go through the same code, so they cannot drift apart. The radio itself is not built. - Every radio check-in is signed. Each responder gets a key when their account is made, and a packet that isn't signed with it is refused before anything is written. There is no transport security on a radio link, so the message has to prove where it came from on its own.
- A gateway program that forwards packets from a pipe or a serial port. It keeps listening through line noise, truncated packets, forged signatures and the server being unreachable, because a gateway that stops on the first bad line is a gateway that is down.
flask --app app sweepfiles reports for anyone gone quiet, so the alarm can be put on a schedule instead of depending on somebody having a tab open.flask --app app node-key <username>shows or rotates a responder's radio key.- Check in with no signal. The check-in is kept on your phone and sent when there is a connection again, judged on the moment you pressed the button rather than the moment it arrived. A pill in the corner says how many are waiting, because a queue you cannot see is a queue you do not trust.
- Sending the same check-in twice is free. Each one carries an id made before it is sent, so a retry after a dropped connection is recognised rather than logged again, and resending an old one cannot make it look recent.
- A written account of what works without a network and what does not, with a table saying plainly which parts are built.
- Export an ICS-214 Activity Log — the form agencies already keep at a multi-agency scene — built from logged records rather than from memory. Every assignment, arrival, check-in and automatic alert, in time order.
- A documentation site under
site/, built from the samedocs/files that live in the repo, so the two can never disagree. Twelve pages, including why this was built, how to run it, what the security model does and does not cover, and what the accessibility audit found, with a light that follows the cursor and transitions between pages that leave the header where it is. All of the movement switches off for anyone who has asked their system for less of it. - Tagging a version builds a GitHub release, with the notes taken from the changelog rather than from commit subjects, and refuses to publish if the tests or the changelog check fail.
- It can be hosted from one file in the repo, so the deployment is reviewable rather than living in somebody's dashboard. The database is rebuilt on every boot, which means every visitor arrives at the same incident with somebody already overdue instead of whatever the last person left behind.
- A banner on every page of the hosted copy, saying it is a demo, that nothing in it is real, and asking people not to type a real address into something that looks like an emergency service and is not one.
- A disclaimer, readable without an account, saying plainly that DiresQ does not contact emergency services and that the triage helper orders attention rather than giving medical advice. The report form carries the warning above its first field, and the triage result carries it beside the category.
- The report form reads what you type and suggests how bad it is and what equipment is needed, showing the words that led it there. It stops adjusting the dropdown the moment you set one yourself, says nothing below the confidence it needs, and never writes anything — the person filing the report decides. Runs on the machine serving the page in about a tenth of a millisecond, with no model file and no network.
- It also notices when a description matches a report somebody has already filed, and offers a link to it. Duplicate reports are how six people end up at one address while a street nearby has nobody.
- A report page now says what the job needs and who is free who has it — "boat: 1 available, r.castillo". The description is read for the equipment, the board already knows who is idle, and before this those two facts never met. Anybody already out is excluded, because offering them is how you pull somebody off a scene they were needed at. When nobody free has the equipment, it says that instead of staying quiet.
- A social preview card, drawn from the palette by a script rather than by hand, so it can be regenerated when the wording changes.
- The map keeps the tiles it has already drawn, so it still shows where you have been when the network goes. It does not download an area in advance — that would only help somewhere you have never looked, which is usually where the disaster is, and the OpenStreetMap usage policy forbids it. The cache is capped, and nothing about the feed or the API is kept, because a stale list of who needs help is worse than no list.
GET /api/modelsays what the classifier is, what it was trained on, and what it is bad at. Public and unauthenticated, because anyone should be able to find out what the software is doing to their report.- Written decisions, build log, architecture notes, known limits and API reference under
docs/. The architecture notes name the point at which each design stops working, rather than only what it does. - An icon, and a
robots.txtthat keeps the whole site out of search results. Live reports name real addresses; none of it should be findable. - A Board link on the feed, the map and every report page. It turns red and shows a count the moment anyone goes overdue, so you learn somebody is late wherever you happen to be rather than only while watching the board.
- Report cards say how many people are on their way and how many have arrived, and carry the staffing signal when someone on scene has set one. A report nobody has gone to reads "0 responding".
- Give a free-text ETA when you join, so the board knows when to expect you. Plain durations work the way people type them: "30 min", "2 hrs", "half an hour", "back in a couple hours", or just "45". The parser refuses anything it is not confident about rather than guessing, caps intervals at four hours, and rounds anything under five minutes up. A refused ETA still lets you join, on the default interval.
- A README covering setup, configuration, every route, the design decisions and the known limitations.
- Resolve a report when it is handled. Open to whoever filed it and to anyone on scene, since those are the only people in a position to know. Resolving clears everyone still attached and drops it out of ...