Repository navigation
Merge accounts (integration-local) into integration; reset links stop signing people in - #48
Merged
Merged
Conversation
Found by screenshotting it, which had not been done -- the effect was written and shipped without anyone looking at it. Two faults, both from hanging the sparkle off the corner at top:-4px right:-3px. It landed in the gap between Save and the next header button, where it read as belonging to neither; and since the buttons are siblings in a row, the next one along painted over it, so most of it was simply not visible. Inset into the button's own top-right corner now, and the button raised a layer so its glow and sparkle draw above the sibling rather than under it.
clj-kondo flagged it. The value was always the literal, so the compiled CSS does not change.
Watched in a real browser for the first time, the heal worked when a character was opened and failed twice in ways the unit tests could not see. A reload never healed. :initialize-db restores the builder's in-progress character with a bare assoc straight into app-db, which never reaches :set-character, so the reconcilers did not run: a character that healed when opened from the list stayed broken after a refresh. Boot now goes through the same set-character, so both paths are one code path. The toast never painted. Every way a saved character reaches the builder dispatches [:route ...] beside :set-character, and :route queues a [:hide-message] that runs after anything :set-character queued, so the message was set and cleared in the same frame. :set-character now only records the heal, and :route announces it after its own clear. dispatch-n is FIFO, which makes that the one place navigation cannot erase the toast it delivered. Builder only, and once per heal. test/browser/character_heal_e2e.js drives both paths against lein e2e-server and asserts the stored key is rewritten, the option renders bound by name, the toast is on screen and the save button carries its cue. It found both bugs, passes 13/13 with the fix, and is registered with the probe runner so a later change cannot quietly undo either. Saving needs a verified account the in-memory server does not seed, so the save-clears-the-cue half stays in events_test.
Restoring the builder's draft on startup repaired it in memory and stopped there. The copy in browser storage stayed broken, so every refresh repaired the same keys again and announced it again: three refreshes, three repairs, three toasts, found by watching it in a browser. The repaired draft is now written back to browser storage when startup repaired something, and only then. That is the builder's own working copy, which it already rewrites on every edit. The character saved on the server is not touched; keeping the fix there still takes Save, and the save button still says so.
Loading sets an invalid entry aside for repair, and it did save that set-aside copy, but it left the entry in the library copy as well. So every load found the same entry and set it aside again, until some unrelated homebrew save happened to rewrite the library. Watched in a browser: set aside on load 1, set aside again on load 2, gone only after an ordinary save. The library copy is now rewritten on the load that sets the entry aside. The order is the safety: it is rewritten only after the set-aside copy is written. If storage is full and that write fails, the entry stays exactly where it was, which costs a repeat on the next load instead of the entry. The ordering lives in persist-set-aside!, which takes the storage writer as an argument, so the test drives a refused write directly. Faking a full localStorage by patching Storage.prototype did not take effect in the CLJS test page, although the same patch worked from a plain page, which is how the real behaviour was confirmed before this was written.
The save banner's "Export this source" link carried a copy of the source from the moment of saving and handed it to the export check. Since export began saving its cleanup back to the library, clicking that link after any later change wrote the old copy over the library: a source turned off came back on, an entry added since was gone, and the file lacked it too. This is the third time this link has broken, each time by trusting what it was handed. The export check now works from what the library holds whenever it holds the source (export-source), and falls back to a copy handed in only when it does not. The banners pass just the source name. The save always lands before the banner can be clicked, so the older bug where the file came out missing the item just saved cannot return by this route. builder_card_export_e2e.js now changes the source after saving and clicks the link again. The stored spell carries curly quotes so cleanup has something to rewrite, which is the only condition under which the old link wrote back. Checked both ways on lein e2e-server: 13/13 with this change; without it both new checks fail, the source back on and the added spell missing from the library and the file.
A content section stored as text or as a list still holds everything that was in it, and nothing recovered it. Salvage kept any section that was not a map exactly as it was, so it loaded silently, its source looked fine, and export later refused the source with no fix in sight. mend-section puts those back in shape: text read back, a list keyed by each entry's own key or the key its name derives, an empty section dropped. It refuses to guess -- two entries claiming one key, or an element that is not an entry, leaves the section for a person. mend-plugin, mend-library and mend-import-data run it over a source, a library, and freshly read import data, including a whole file stored as text. What nothing can read is now set aside instead of kept: a damaged section, and a whole source that is not a map. Salvage used to drop the latter from both sides, which a write-back of the library would have turned into an erasure. merge-plugins lets the newer value win wherever either side is not a map. Merging set-aside data called merge on text, which throws, and it runs while the app starts. For sections that are maps the result is unchanged.
Import now mends right after reading the file, before any other step walks the data. A section stored as text or as a list imports with everything in it and exports. A file whose whole content was stored as text used to crash inside the cleanup steps and import nothing, with no message. The notice adds "Repaired N damaged sections", the import log lists each repair, and a section nothing could read no longer counts as an imported item -- it said "Imported 1 items" for one. Loading mends before salvage and writes the repaired library back through the same ordering as the set-aside write-back, so each repair happens once. A section nothing could read is set aside, and the import notice and the needs-attention panel both count it. The panel names the damaged section and offers Export raw and Discard, since there is no name to type for it. Both used to count only entries, which would have read "0 entries couldn't load". Checked on lein e2e-server with every shape imported through the real import event: 28 of 28, including the startup repair written back on the first load and nothing left to repair on the second.
An import that skipped items arrived in the green success card with a check mark, so the one thing worth noticing looked like good news. It said "Skipped 1 invalid items" and sent people to the browser console. A partial import, or one with key clashes, now uses the warning card. Its lines carry a green check for what came in, an orange triangle for what was skipped and a wrench for what was repaired. The colour sits on the icon, not the text, so the text stays readable on the tinted box. The skipped line points at the import log, which lists each skipped item and why. Counts read "1 item" and "2 items". A clean import keeps the success card unchanged.
An item with no name showed as its bare key, ":no-name", which reads as code. It now shows as the placeholder Export & Auto-Fix will give it -- "Unnamed Spell", "Unnamed Monster" -- with the key beneath it in small type when there is one. The header also said "1 item need attention"; it reads "needs" now.
…er open menus The round import-log button sat in the bottom-right corner of every page, even with nothing in the log. On a laptop-height window the My Content menu reaches that corner, and the button, drawn at z-index 900, covered the menu's last item: clicking Selection Builder opened the empty log instead. The button now appears only when the log has something in it, or while its panel is open so the panel can be closed from it. Its z-index is 150: under an open header tab (200), still above the sticky button row (100). The panel it opens stays at 950. Its styles move from inline to garden. header_menus_e2e.js runs the shortest window a second time after a real partial import puts the button on the page. With the old z-index that pass fails on Selection Builder; with this change all 30 checks pass.
… is none Twenty-six probes carried their own copy of findChrome, a dozen variants between them. All of them looked only for chrome-linux/chrome, which Playwright 1.57 names chrome-linux64, so none found an installed browser by path. Five of the copies threw when /opt/pw-browsers did not exist. Eight more probes skipped the finder and hardcoded /opt/pw-browsers/chromium or read one env var each: CHROME, CHROME_PATH or PLAYWRIGHT_CHROMIUM. Outside the web sandbox those failed at launch unless all four variables were set by hand. test/browser/lib/find-chrome.js is now the one copy, used by every probe and by the CLJS test runner. It takes a path from any of those env vars (or E2E_CHROMIUM, which scripts/e2e/run.js reads), a binary or an install folder. Then it tries the newest chromium-<n> under PLAYWRIGHT_BROWSERS_PATH or /opt/pw-browsers, in either folder layout, and otherwise leaves it to Playwright's own browser. A variable that points at nothing is named on stderr rather than silently used. run-browser-probes.js resolves the browser once. With none, each probe is a SKIP naming the fix, not thirty identical launch failures; STRICT still fails the run. Checked with no browser variables set: 14 of 14 probes pass, and header_menus runs 30 of 30.
… just once A library that reads back as text of itself loaded nothing: the loader saw a string, copied it to plugins:corrupt and moved on. mend-library now reads it back when the text is a map keyed by source names, and the load writes the library back, so the repair happens once. A stored value that is still not a library was copied to plugins:corrupt on every load, because the active slot was never cleared; the unreadable-text case beside it always cleared its slot. It is now cleared as well, but only once the copy is saved, so a full storage cannot turn the copy into a loss.
Save anyway with placeholders in the selection builder derived the key straight from the name. A name like "9 Lives" gave :9-lives, which the save accepted and the next load set aside, so the placeholder save undid itself on refresh. The other builders run sanitize-item-names first; the selection builder now does too.
… kept Resolving an import conflict can rename or disable the copy already in the library. Those changes sit in the base library handed to the import store, and the store only wrote the library when at least one incoming entry was kept. When every incoming entry was set aside, the renames and disables went with it, in storage and on screen.
… export fixes_persist_e2e.js applies each repair through the real app, then reads localStorage, reloads, confirms the reload has nothing left to repair, and exports where there is a file. Paths: import cleanups, the loader's repairs and set-asides, a library stored as text, a stored value that is not a library, conflict renames, Auto-name & Restore, Export & Auto-Fix, Save anyway for a spell and a selection, and a character repaired on refresh. Against the build before the two load fixes and the selection fix it passes 25 of 33, failing exactly those paths; with them, 33 of 33.
An entry stored as text, or anything else that is not a map, reached the step that fills missing fields. That step assoc'ed into it and threw "No protocol method IAssociative.-assoc defined for type string", so the import stopped partway. It now leaves such an entry alone; validation skips it and the import log lists it, as with any entry the import cannot use. The full-import tests used an entry with no source as their broken example, which the next commit makes importable, so they use an entry that is text instead.
The July rule sends content with no source to Default Option Source, but only an :option-pack that was present and blank or nil got filled. An entry that left the field out was skipped on a single-source import, counted as imported and then set aside on a multi-source one, and set aside when found in stored homebrew. An entry with no usable :option-pack now takes the name of the source it is filed under. A single-source file has no source key, so there it takes the one source the other entries name. Only when there is no name to take does it get Default Option Source, as before. Import and load share the rule (e5/source-for), and the loader writes the filled library back with its other repairs, so it happens once. single-plugin-source now asks e5/declared-source, the same reading of which source a file's entries name. The persistence probe checks the import and the load path (35 of 35). The load test and the header-menu probe used a sourceless entry as their broken example and now use an entry that is text. The import cleanup table in ORCBREW_FILE_VALIDATION also said a nameless source became Default Option Source; it becomes Unnamed Content, as decided in b5ba38d.
A stored race whose :key was text left every page on the loading spinner, with only the rescue download bar. At startup core calls init-template-cache!, which builds the character template. The races subscription keeps races in a set sorted by key, and compare threw comparing :dwarf with "text". That throw happened outside render, where no error boundary can catch it, and the rest of core never ran, so the app was never mounted. Two layers. Each startup step in core runs through boot-step, which logs a failure and carries on, and the template cache catches its own failure; the save handler already works without a cache. The sorts that see homebrew keys or names use common/safe-compare, which orders mixed types by type and then by printed form instead of throwing: races, spells, class options, subclass names, template options and the spell list. boot_rescue_e2e.js stores such a race and checks the app still draws. It failed on the build before this change and passes on this one. A sweep of wrong types and shapes through import, reload, the template and export went from 43 crashes to 31: every key crash is gone, and what remains is traits and options lists, and a source that is not a map.
…port crashing on them A sweep of wrong types and shapes through import, reload and export found 31 crashes left, all in three places: - A :traits list (classes, subclasses, races, subraces, backgrounds) or a selection's :options holding something that is not a card. [42] or ["text"] made the import throw with nothing saved and no message; a bare 42 imported and then made export throw. - A source that is a number or unreadable text made the import throw. - A :key that is not a keyword, which used to stop the app starting. mend-plugin now repairs inside entries as well as sections, so import and load share it and a load writes a repair back once. A :key that is not a letter-first keyword takes the key the entry is filed under, which keeps characters that point at it working. A card list keeps its cards, turns text into a card with that name, drops anything else, wraps a lone card, and is removed when it is none of these. :options is only treated as a card list on a selection. A healthy entry is not touched and reports no repair. The import steps that walk every source or card now step past ones of the wrong shape, so a non-map source reaches the store, which already sets it aside. The import notice counts damaged entries apart from damaged sections, and the import log names each repair.
… still breaks Startup built the character builder's whole template before anything was drawn, outside every error boundary, only so autosave had a cached copy. A throw there stopped the app from mounting. Startup now reads storage and builds nothing from it: - The template cache starts on the first character save (ensure-template-cache!), from the sheet, list, parties or combat tracker. A save retries while it fills and says so if it never does, instead of skipping silently. - If loading the library itself throws, core copies it to plugins:corrupt (clearing the slot only once the copy is saved), starts without it and shows a notice with a download. If startup fails without homebrew too, the library is put back. For an entry no repair can mend, each conversion into a character option is guarded (homebrew-guard): races, subraces, backgrounds, feats, classes, subclasses and spell lists. A throw skips that entry and reports it; the report sets it aside in plugins:rejected, which is saved, and a notice names it with a link to My Content. Option builders return lazy sequences, so some damage only throws when a page draws the option, outside the guard. Fully realizing every entry to catch that costs about 2 s with a 12-source library against about 40 ms unrealized, so the everyday path does not. A deep check (homebrew-check) realizes entry by entry, only: after an import, for the sources it changed; after a Restore, for that source; and when a page has already failed, over everything, after which the page tries again. The conversions it runs are registered by the subscriptions, so the events namespace still does not load them. The startup notice's download goes through save-orcbrew-blob! (new :raw? option) and is declared as a hatch in export_paths_test, like the other unvalidated downloads. The error boundary's retry never redrew the page: Reagent does not re-render on React state alone, so it now forces the update. A spell whose :spell-lists is not a map is repaired on import and load like the other entry damage. homebrew_safety_net_e2e.js makes one race throw on conversion and another throw only while drawn. With this change all 16 checks pass; on the build before, 1 of the first 13.
…into integration-local
The Orcpub#669 auth change moved get-auth-token into event-utils and updated the calls it knew about. :report-character-problem, the email support button on the character-load recovery panel, arrived later and still called the local function, which no longer exists. Clicking it threw, so the report was never sent and the button stayed on sending. Lint reported it as an unresolved symbol. The new events test dispatches the report and checks the token it sends; it errored before this change.
Moving ::mi5e/custom-items onto reg-api-sub and switching off the remote-item chain left seven referred vars unused: reg-sub-raw and dispatch, url-for-route, auth-headers and handle-api-response, <! and go. Lint went from 10 warnings to 17 with them.
The comments kept the constraints: why the item loader does not redirect on a 401, that the parties list is stored under ::char5e/parties, that a filtered list written into db stops following its source, which pieces of the switched-off remote-item chain must be restored together, and that the old guard read a key that is always nil. They lost what the code or git already holds: commit hashes and dates, the audit narrative, a diagram of the chain, notes on removed code, instructions to write KB entries, and product questions about item sharing, which belong in the KB. The switched-off chain's comment now says plainly that GET /api/dnd/e5/items/:id has no check-auth and returns any owned item to anyone, which matters before anyone wires that chain up. The e2e loader script no longer names another branch.
One probe per link between homebrew items: does a key rename (the one import conflict resolution and relocation use) carry the link along, and does the consumer actually read it. The first run asserted the correct outcome everywhere; eleven of thirteen rename probes failed, and those are now pinned as GAP rows that turn red when a fix makes them follow. Only subclass -> class and subrace -> race survive a rename today. Also pins two facts a code-reading sweep got wrong: race :props :language and race :spells are both live grants. And a stranded feat race prerequisite does not ungate the feat; it locks it for every character under a blank " Only" label.
A rename happens only because another item holds the key, so a stranded link latches onto that other item rather than dangling. Run through the consumers the builder reads, in the import case: an imported spell joins the library's class list and its own class offers nothing; an imported feat requires the library's race; an imported class's level choice offers the library's selection with the library's options.
…r' into integration-local
Three conflicts, all both-sides-kept. The selection save-anyway keeps this branch's name sanitizing and takes the source-tagged key, minted from the sanitized name so a name like "9 Lives" cannot derive a key the next load sets aside. The two test files each gained different tests on both sides.
The source-tagged-keys merge kept this side's ns form, which lacks the testing refer its tests use, and a local k in moved-through shadowed the k helper that branch added.
…lsewhere Merged beside feat/source-tagged-keys, its relaxed save-collision treated an item as back in its own slot by key alone, so retargeting an item's Option Source Name to a library that holds the key silently overwrote that library's item (retargeting-the-source-must-not-destroy-the-item- sitting-there failed). The duplicate save gating is being reworked on its own branch, with the builder remembering which source an item was opened from; it comes in with that.
Brings save-gate (#37: builder origin, move-or-refuse saves, the shared library-links model that repoints every link on a key change) and comment-debt (#39). Where both sides changed one place: - homebrew save, collision and links: integration's, which supersedes the two relocate fixes and the gating revert made here - accounts, auth and sharing: this branch's (share line, party share tokens, image-probe limits, reg-api-sub logging out on 401) - the library loader keeps this branch's repair step (mend-library, persist-set-aside!, fail-soft startup), which integration never had; integration's additions beside it (relinks, revision, cross-tab watch, pre-fix copy, builder origin) are all kept - combined: party add/remove answer with the party after the write; imports and restores still run the homebrew build check through commit-library; the selection save-anyway sanitizes names under the save gate; export reads the live library and no longer writes back; an import that keeps nothing still stores renames of existing items - the heal toast stays in :route, where a route change cannot clear it - dropped a doubled set-aside notice the auto-merge produced
Integration's comment check (#37) fails docstrings over 6 lines, comment blocks over 4 and history phrasing. 77 hits here, all from code written before the rule. Docstrings are spec now, with a GOTCHA where a reader would otherwise write a bug; the rationale moved out, and each trimmed comment names the KB page that holds it (account-flows.md, homebrew-safety-net.md, share-links.md, homebrew-keys-design.md). Code is unchanged, checked token by token against the merge commit. Also marks the startup write that restores a set-aside library as part of the library load, the one write outside the gate the gate test allows.
set-aside-broken-homebrew wrote the library directly, bypassing the gate's revision count and cross-tab merge, which library_gate_test now refuses. It writes the set-aside copy first as before, then commits the smaller library through commit-library with :deleting?, since removing the entry is deliberate. If storage refuses the set-aside copy, the entry now stays in the library and the author is told; before, it was removed for that session only. Also trims this file's comments to the comment rule.
Integration's heal of a renamed pick waits for the template's offered keys, and integration built the template at startup. This branch builds nothing at startup, and only a save asked for the template, so merged, a character with a renamed pick stayed unhealed until it was saved. :set-character and the routes to the builder and to a character's page now ask for the template; other pages still build nothing. A heal that lands when the template arrives comes after the route change, so it announces itself through ::e5/announce-heal, which shares one rule with the :route announcement (heal-announcement). Measured with scripts/e2e/template-on-open.js, three runs each: opening a character heals in 3 ms (before: not within 15 s); login and the character list build nothing; the builder's first load goes from about 1.05 s to 1.22 s, the template's cost moving from the first save to the first builder visit. test/browser/character_heal_e2e.js passes 13/13. The heal tests now follow integration's model, and the test runner registers the template request as a no-op so the watcher it starts cannot dispatch into later tests.
The template's 230-320 ms build ran just after the builder appeared, so the page was briefly unresponsive right as it showed. On the builder and character pages it is now built at startup, before the app mounts: the spinner stays up about a quarter second longer and the page is responsive when it appears. Measured over three loads: the template lands before the spinner clears, and what follows is the usual 60-150 ms of drawing. Other pages still build nothing at startup; the step is guarded like the lazy build, so a failure leaves the cache empty.
The stale-bundle check kept its own list of source folders, and the list missed web/cljs, so startup edits were tested against an old bundle with no warning. It now reads :watch-dirs from the build config of whichever bundle is on disk, the same list the compiler uses, and stops the run if it cannot read it rather than skipping the check. Proved by making web/cljs/orcpub/core.cljs newer than the bundle: the run names it.
The boot spinner was spiral.gif: a 60 KB separate download, stair-stepped against the gradient. It is now drawn inline by the server, so it shows with the first bytes of the page, picked at random per load for now so all three get seen; ?spinner=spiral|d20|spell forces one, and anything else falls back to random. The shapes are computed at load (orcpub.loading-spinner): the spiral's curve, the d20's twenty face placements, and a ring of made-up runes. Numbers are formatted with Locale/ROOT so a server locale cannot write 100,5 into the SVG. Only transform and opacity animate, each moving piece its own element, so all three keep moving while the app builds: measured on the real page with the main thread blocked for two seconds, the spiral and spell circle stay smooth and the d20 drops to about 10 frames/s.
f185220-accounts carries the September hotfix batch (share links, Stop sharing, heartbeat, items load with the character, parties keep shared homebrew, the first e2e harness), the garden harvest, the mobile layout, one duplicate-key fix and the accounts redesign (password rules, breach screening, reset keys stored as digests, sign-in notices, sign out everywhere). PR #34. Conflicts (9 files): - run.sh: integration's runner. Accounts' mail sink, E2E-READY wait and seeded character IDs moved into server.sh and run.sh; its no-default-suite rule kept as a usage listing. A stray block that rejected any suite outside scripts/e2e (and --describe) was dropped. - routes_test.clj, .clj-kondo/config.edn: both sides' additions. - views.cljs: integration's supporter-slot; accounts' redesigned sign-up form. - events.cljs :register-failure: accounts' per-field server errors, and every other failure as a :general notice, so none is silent (the locale-safety fix, kept). - styles/core.clj: integration's (already holds accounts' lines plus later fixes). - index.clj: orcpub.env plus the spinner require. ENVIRONMENT.md: both. Archived spike: ../lib. Beyond the markers: accounts' password rules, email-domain check and breach-check setting pinned to Locale/ROOT (a Turkish server judged passwords differently from the browser). The two registration test files stub the per-host sign-up throttle, which counts across the whole JVM and refused the 11th test sign-up. auth-flows.js gets its own server.
Opening the link minted an hour-long session for the account, so whoever held the mail was signed in without changing the password, and the owner got no notice. Now: - the reset page sets no session, only a plain reset-username cookie for the strength meter and Referrer-Policy: no-referrer, since the key is in its URL; - the form submits the key, and reset-password checks it at submit (unknown, expired and used are one answer) instead of trusting a session; the route no longer needs one; - the key is retracted in the same transaction as the new password; - index-page-response keeps the caller's headers instead of replacing them. No auto sign-in afterwards: the success page already sends people to log in. Tests: a-reset-link-is-a-one-time-permission-not-a-sign-in, an-expired-reset-link-sets-nothing; both fail on the previous code.
Its Highlights joins the release summary as a third sentence; 15 bullets written before the house-style lint are trimmed to it; the reset-link change is recorded.
…hare pair, one run at a time 12 suites read re-frame or cljs.core and only ran on a development bundle; they now say so. The two share suites that share and unshare the same seeded character get their own server: in parallel one shared it while the other expected it unshared. fixes_persist checks that the bad value left the active slot, which then holds the app's default empty library. run-all.sh takes a lock, so a second run fails at once instead of quietly sharing the first's server.
…accounts Six conflicts: config.clj and equipment_subs.cljs keep accounts' versions, which already carry #47's renames; events.cljs takes #47's structure with accounts' two additions inside it (clear :homebrew-reported, re-check builds after a restore); the lifecycle test takes #47's rename; the CLJS runner lists both new namespaces; CHANGELOG.md keeps both folded blocks.
The author-email rewrite gave the accounts commits new hashes; 110 references follow them. Five hashes in the accounts changelog never existed (a1b7e3f9, f2c8d4e1, 8e3f5a21, f4a2d1c8, e8f6b2a1) and now name the commits they describe; two bullets had their references swapped. The reset page reads the username from a cookie it sets, not the session token it no longer creates.
|
- reset-password: the write is conditional (:db/cas) on the reset the request read, so two submits of one link set one password; the loser gets the spent-link answer. - The reset page sends the password as its own confirmation while it is revealed; the server still compares the two and refused every revealed reset. - A short link's homebrew applies only while the page is on the character and token it was fetched for (::e5/apply-shared-homebrew-if-current). - put-share writes only while the share still has the token it checked (:db/cas), so an upload racing New link or Stop sharing cannot land on the deleted record. odb/cas-failed? tells a lost race from a real failure. Both server races have a test that replays them with an older database snapshot; each fails on the previous code.
The server JVM and the memory sampler inherited the lock's descriptor and held it a moment past the run, so a run started right after was refused.
Owner
Author
|
@greptileai review |
codeGlaze
added a commit
that referenced
this pull request
Oct 5, 2026
codeGlaze
added a commit
that referenced
this pull request
Oct 5, 2026
A second exit rule (from 57d0c7c, reached integration with #48) required a 'Ran N tests' line, which the full-suite page never prints (it reports 'Totals'), so every full run exited 1, green or not. The exit now uses the summary verdict computed just above, plus no FAIL/ERROR. Checked both ways: 606 passing tests exit 0; one planted failure exits 1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the accounts batch (
integration-local, formerly PR #34) into integration, with the reset-link change agreed on 2026-10-04. Supersedes #34.What arrives
Reset links (new here,
f8f270dd)A reset link no longer signs anyone in. Opening it used to mint an hour-long session, so anyone holding the mail was signed in without changing the password, and the owner was never told. Now, per OWASP's Forgot Password cheat sheet, the link is a one-time permission to set a password:
reset-usernamecookie for the meter, plusReferrer-Policy: no-referrer.reset-passwordchecks it on submit, so a mail scanner that opens links spends nothing.Tests:
a-reset-link-is-a-one-time-permission-not-a-sign-inandan-expired-reset-link-sets-nothing. Both fail on the old code.Conflicts and merge-time fixes
run.sh: integration's runner wins. Accounts' mail sink, E2E-READY wait and seeded character IDs move intoserver.shandrun.sh. A merged-in block that rejected any suite outsidescripts/e2ewas dropped.:register-failure: keeps accounts' per-field server errors, and turns every other failure into a:generalnotice, so no failure is silent. That is the locale-safety fix, kept.Locale/ROOT. Without that, a Turkish server judged passwords differently from the browser.run-all.shtakes a lock, so a second run fails at once.History
37 commits authored as
claudecode@eliminater.comwere rewritten togithub@codeglaze.combefore this branch was ever pushed. Trees are identical and every commit is signed. As a result, the commits here have new hashes, compared withf1852203-accounts.The changelog's 110 references were remapped to the new hashes. Five hashes in the accounts changelog never existed; they now name the real commits.
Four references in integration's own changelog were already dangling before this branch (
26f8c602,2a6fde93,2e13d2b2,429152e). This PR leaves them as they were.Verified locally
lein testauth-flows43/43, which covers the new reset flowLocale and Windows runs are in CI.