0.5.0 - 2026-10-28 Added A screen reader hears what changes where it is not looking. The session's status and its verdict, a command that failed during a session, the result's verdict as the result screen opens, a history that could not be saved, the calculator's answer and its error, the line under the application filled in from a shortcut, and what a copy button did ("Copied", "Clipboard busy") are announced as they change. A failure cuts in, the rest waits for the reader to finish. The calculator's answer is announced once a number being typed rests for half a second, not for every key, and coming back to a screen announces nothing that has not changed. The session clocks, which change every second, are not announced. A shortcut (.lnk) can be the application. Picked with Choose… or dropped on the window, a shortcut fills the form with the program it starts, the arguments it hands it and the folder it starts it in, and a line under the application says so, because the arguments and the folder stand under Speed and options. A shortcut that leads to no program file - one an installer made, or one to a document or a script - is refused with the reason, and so is one whose program is gone. Nothing starts until Start. The file picker used to swap a picked shortcut for its program in silence and drop its arguments and folder. The window takes the application from its command line. ChronoMock.exe fills the form as a drop on the window does, so "Open with" in Explorer and a program dropped on the window's icon land in it. A relative name is read from the folder the window was started in. A file that cannot be the application is refused with the reason a drop gets. Nothing starts until Start. CHRONOMOCK_DATA_DIR moves the window's own folder. Set to a full path, it holds the session history and the diagnostics logs in place of %LOCALAPPDATA%\ChronoMock, for a test run that must leave the history of the account it runs under alone. A value that is not a full path, or that holds a character no folder name can have, stops the window at start and names the value, rather than write into the folder the window was started from or fail later without naming the variable. The window's own tests set it, so a fault they raise on purpose no longer leaves a diagnostics log in the folder of the account that runs them. "Copy as command" under the calculator's result copies the question it answers as a PowerShell command line: the engine by its full path, so it works from a portable copy that is not on PATH, and the same arguments, so a terminal gives the same answer. Windows PowerShell 5.1 passes three kinds of value on wrongly - one with a double quote in it, an empty one, and one with a space that ends in a backslash - so a command holding one of them starts with a comment that says to run it in PowerShell 7.3 or later. chrono calendars lists the calendars the calculator can count in, the way chrono presets lists the presets: every calendar file --calendar reads, with its country and its own name, and every file it would refuse, with the reason. --dir reads another folder, --json answers in chronomock.calendars/1. The window's calculator now offers the calendars this command lists, so a calendar added as a data file can be picked there too. A Chromium session says what its pages did that the fake clock does not reach. Three new warnings, each said only when it happened, in a Chromium session and for the pages of a web engine inside an application alike. chromium.engine_stamps_real: a page's animation frame got its stamp, or the page read Event.timeStamp or document.timeline.currentTime, while its timers ran faster - the times the browser stamps on frames and events run at real speed, so they disagree with performance.now. chromium.animations_real: a page played CSS or Web Animations while its timers ran faster, and they played at real speed. chromium.contexts_unread: pages or workers ended before the audit, which reads once a second, could read them, so what they called is not in the counts. The verdict does not change. The shim notices the stamps without changing what the page gets. A GitHub button in the window's title bar, beside Donate. It hands https://github.com/donislawdev/ChronoMock (the source, the releases and the issue tracker) to your browser, and its tooltip says so before you press it, the same way Donate does. Chrono Mock itself still opens no connection. If Windows has no browser for the address, the window says so and shows the address to open by hand. The report says when timers could not keep the session speed (timer.interval_floored). A window timer (SetTimer) never fires more often than every 10 ms, a limit Windows sets, and a periodic waitable or thread-pool timer is never given a period under 1 ms, so at a high speed such a timer runs slower than the rest of the session. Waits held at their floor were already reported (wait.timeout_collapsed), timers were not. The evidence file names the run, not only the time it ran at. Under the requested: line, chrono run --report now writes the version and the core's bitness, when the run started (UTC, from the real clock), the target, the folder it started in, how many arguments it got, and the switches the run was given. The arguments themselves are left out unless the new --report-args asks for them, because an argument can carry a password and the file is made to be handed on. --report-args without --report is refused. The date analyser reads the dates Chrono Mock prints. chrono calc --analyze now reads an ISO date and time with a fraction of a second and a UTC offset, such as 2026-01-01T10:00:00+02:00 or 2026-01-01T10:00:00Z - the form the calculator prints itself, which it used to refuse as a bad second. A date with an offset is a point in time, so it is shown in the session zone, like a pasted Unix timestamp, as the new reading iso_offset. A fraction is cut to the second. A date whose instant falls outside the years the calculator computes on once it is shown in the session zone is refused. Three more time functions are covered: QueryInterruptTime, QueryInterruptTimePrecise and QueryUnbiasedInterruptTimePrecise. Under --scale-duration they run at the session speed like the tick count and QueryUnbiasedInterruptTime, and the audit lists them. The two interrupt-time clocks keep the real time the computer has slept since it started, as Windows does - sleep is never multiplied by the speed. This changes what an embedded Chromium engine in WebView2 reads under --scale-duration: it imports the third one, which used to stay real beside a scaled tick count. The audit now has 44 channels. The session log says when the computer slept. The session clock stands still while the computer sleeps, as during a pause. A new host_slept event says how long it slept, at the first heartbeat after waking, session_verdict carries the total as host_slept_ms, and the report and the window warn time.host_slept. The report says when a timer for a set date and time may fire at the wrong moment of the session. Such a deadline is turned into a delay when it is set, at the speed of that moment, so one still pending when the session jumped or changed speed fires where the old clock would have reached it. The new warning timer.absolute_due_converted appears only when that can have happened. The report says which browser a Chromium session drove. session_verdict carries what the browser calls itself (browser, such as Chrome/152.0.7977.149), and the report and the evidence file have a browser: line under the target. A native session leaves it out. The report says when a page or worker was already running when the session reached it (chromium.context_reached_running): a page whose own scripts were in it before the time shim, or a worker that was not paused on start. Its scripts may have read the real clock first, and a timer it set before keeps its real interval. Said in a Chromium session and for the web engines inside an application, whose pages usually load before the session finds them. The report says when an application left running will open a debugging port later (embedded.variables_outlive_session). With the web engine channel on, the application keeps the setting that makes its engine open a debugging port, so a web view it opens after the session opens one too, reachable by other programs on the computer, until the application is restarted. The report says when a web page's timers could not keep the session speed (chromium.timer_rate_capped). A browser runs a timer set from inside another timer no more often than every 4 ms, so at a high speed a timer that would have to run more often than that runs slower than the session - the clock itself keeps the speed. Measured on Chromium: from the seventh timer of a chain, and from the seventh tick of a repeating one. The report says when a frame of a page was out of the session's reach (chromium.frames_unreached): a frame of another origin of the same site, a sandboxed frame, or one that was in the page before the session reached it. Such a frame keeps the clock it started with (the real one, if it was there before the session), does not follow a jump or a change of speed, and its calls are not counted. Changed The three holiday calendars were checked against the law again on 28 October 2026. Nothing changed since the check of 6 October: the Polish Non-working Days Act was last amended with effect from 1 February 2025, no public law enacted since then touches the federal holidays of 5 U.S.C. 6103, and the Federal Reserve's holiday schedule for 2026-2030 is still the one of 8 July 2026. The days stay as they were, and only law_as_of moves to 2026-10-28 in pl, us-federal and us-banking. Every copy button says on itself what its copy did - "Copied", or "Clipboard busy" when another program held the clipboard - for a moment, then reads as itself again, and it never changes width while it does. The result's summary and diagnostics shared one line beside the buttons that never cleared, so a second copy changed nothing on the screen, and the calculator said nothing at all when the clipboard was busy. In the calculator a format's Copy now stands beside the format's name, and the value under them has the whole width of the column. A portable copy keeps its session history and diagnostics logs in %LOCALAPPDATA%\ChronoMock, as an installed copy already did. Beside the program, on a shared folder or a stick passed round, they held everybody's sessions - the applications tested and the arguments they were started with - where anyone with the folder could read them. The history an older version kept beside the program is read in once, when this version first starts, and left where it is. Older diagnostics logs beside the program stay there. Both scenario lists search the same way. A word typed into the session's scenario search now finds a scenario by the line that says what it tests, as the calculator's search always did - both lines are shown on every row. The calculator's search box is the session's: it has the clear button, Enter chooses the first scenario left, and a search that leaves nothing says so in the list's place. The scenario chosen in the calculator stays marked while it still matches, and is marked again when a search that hid it is cleared, with its parameters as they were typed. A jump to an absolute moment that names no moment gets the same answer on both mechanisms. A protocol client that sent {"kind":"absolute"} without local was told moment.invalid by a Chromium session, while a native session read the missing moment as an empty one and, when the same command named a zone outside the range, answered time.bad_zone. Both answer moment.invalid now. The window and chrono run always send the moment, so neither of them could get here. A preset file that puts the target_file_creation hint on a parameter that is not a date is refused when it is read. The hint fills a date, so such a file is wrong whatever values are given. It used to be refused only when the hint was needed, and then as a bad value, so chrono calc blamed --param for a fault in the file. A number on the chrono run command line is digits and nothing else, as it is in chrono calc. The multiplier of --mode xN, --ticks, --timeout, both numbers of --set-after and the tick of --jump-after took a leading +, so --ticks +5 ran five heartbeats and --mode x+60 sixty times faster, while calc refused the same sign. They are refused now with "(digits only)" at the end of the message, and so are x-5 and --set-after 3:-3, which used to be told the multiplier must be at least 1. A number that is only too large is refused without that note. A Chromium session covers every page and worker, however many there are. The audit gives the first 256 a row of their own and counts the rest together in the last row, and every one of them runs on the session clock. Past 256 they used to be refused the time shim and ran on the real clock, which an application that starts a worker per task reached within minutes, and the verdict said partial. The warning chromium.context_ceiling_reached is replaced by chromium.contexts_pooled, and context_count counts every context. The frames of a web page follow a jump and a change of speed. A frame of the page's own origin started on the session clock but kept it as it was when the session jumped or changed speed, and its calls were not counted. It now moves with the page, and its calls count in the page's row. A frame of another site, which the browser runs as a page of its own, read the real clock: the time shim was tried before the frame had a document, failed, and was taken back. The frame now gets the shim with its first document and has a row of its own, counted as covered once its shim answers. A repeating timer in a web page takes a change of speed from its next tick, and keeps its cadence. It used to keep the cadence it was armed with, and a delay was cut down to whole milliseconds the browser's way: at x60 a one-second timer ran 4% fast (960 ms of session time between ticks, measured). A tick missed whole is not made up. The warning chromium.rate_change_affects_running_timers now says what is left: a one-time timer already waiting finishes its wait at the old speed. At speed 1 a one-time timer goes to the browser as the page set it. performance.timeOrigin follows the session clock in a web page, so performance.timeOrigin + performance.now() equals Date.now() as it does without a session. performance.toJSON() keeps the browser's own value. The time shim keeps its clock to itself. The __chronomock object in a page is no longer an enumerable global with the clock in its fields: a page's own scripts do not see it among their globals, cannot replace it, and reach only the methods the session uses. A page still holding the shim of an older version is still moved, released and read. A relative jump in a Chromium session keeps the milliseconds of the clock, as in a native session - it used to step through the clock's text and lose up to 999 ms each time. A jump of a kind neither mechanism knows is refused with moment.unsupported_kind in both, where a Chromium session read it as absolute. A change of speed is no longer reported as a caveat about running timers when no page or worker was there to have one. Every answer to start carries the id the client gave it, the verdict and an error before the session runs, where it used to say 1 whatever was sent. Both built-in clients send 1. The protocol's last-resort error line carries the protocol version rather than a fixed 1. A Chromium session's clock stands still while the computer sleeps, and a correction of the system clock no longer moves it, as in a native session. It used to run on through a sleep at the session speed: one hour corrected on the system clock moved the session clock sixty hours at x60. The pages are given the clock again when they stand more than a second of session time from it, the session logs host_slept, and the report warns time.host_slept. A page that runs a script in the moment between waking and that correction reads the moved clock. A call count stops at 1 048 576 and is then shown as "at least". Every clock call used to write one shared counter, which cost a target with many threads several times the price of the call itself (86.6 ns against 14.1 ns at 8 threads, measured). coverage marks such a count with capped, and the report, the window and the copied summary say "at least". The audit table's count column now takes the width its numbers need. GetSystemTimePreciseAsFileTime moves at the resolution of the timer hardware. It used to move in steps of one timer interrupt multiplied by the speed - 47 ms on average at x60 on the measuring machine, 0.94 s at the default timer. The session's anchors are now read from the precise clock (QueryUnbiasedInterruptTimePrecise, which also leaves sleep out), while the plain clocks keep reading the plain one, so the precise wall clock is never behind the plain one, as in Windows. It costs about 26 ns more per call on 64-bit and 38 ns on 32-bit. A timer or a wait set for a date and time follows the fake clock in every session. SetWaitableTimer, SetWaitableTimerEx, SetThreadpoolTimer, SetThreadpoolTimerEx and an absolute NtDelayExecution are converted without --scale-duration too: a timer set for "tomorrow 03:00" on a clock at 2038 used to wait twelve real years. Relative delays and periods change only under --scale-duration, as before. Another running session ends a start with exit code 7. It used to be exit code 3, the code of an internal error, with a line saying the core ended without a verdict - so a pipeline could not tell a busy machine from a crash. It is the one start failure that waiting fixes. The error event carries the same 7. A run whose evidence file was not written ends with exit code 8. When chrono run --report reached a verdict but could not put the evidence at the path it was given - the disk was full, or another program held the old file open, which then stays as it was - the exit code was still the verdict, so a pipeline keeping that file as the proof of the run could keep an older one under exit code 0. It now ends with 8, and says which verdict it replaces. The verdict is still in the report on standard output and in the temporary file the run names. A code that is no verdict (1, 2, 3, 5, 6, 7) stays as it was. A works session can still be unreliable evidence. The evidence file and the copied report now lead with the unreliable-evidence banner also when the application read none of the clocks the session replaces (coverage.session_clock_never_read), or is a Go program whose wall clock no hook reaches (runtime.go_wall_clock_unreachable). The verdict and the exit code stay as they were. The window's result screen shows those warnings under the verdict's reason, where it used to keep them folded inside the audit. The web engines line claims pages only when some were put on the session clock. It said that the engines' pages ran on the session clock whenever an engine was reached. When the session reached an engine but put none of its pages on its clock that way, the line, the window's note and the copied report now say so, and the window's process table no longer marks that engine's renderer as one whose pages were reached. Each engine in session_verdict.engines now carries its own context_count, and the process table and the warning about a renderer read that one, so with two engines reached and pages in only one of them, the other engine's renderer is no longer said to have had its pages reached. A core older than this field reads as zero, which claims no pages. A refused session names the way to run anyway. Under the refusal the report now says that --force starts the application regardless, and that the report then marks the session unreliable. The audit no longer claims a time function that part of the application reads past. Most time functions live in kernelbase, and kernel32 only passes the call on, so Chrono Mock places its hook where the two meet. When it cannot - another program hooked kernelbase first, or a Windows build where kernel32 has a copy of its own - the hook stays on kernel32: a program that calls kernel32 still reads the session clock, but the C runtime and anything else that goes to kernelbase directly read the real one. The report used to list such a function as covered. It now lists it as not covered, so the session works partially (exit code 10). On the Windows builds measured so far, kernel32 leads into kernelbase for every one of these functions. The audit follows 4096 processes in a session instead of 256. A session counts every process it ever ran, not the ones running at once, so an application that starts a short helper every ten seconds went past 256 in about 43 minutes and the rest of the session's processes were left out of the audit. 4096 takes that to about eleven hours. The processes past the limit are still put on the session clock, and the note about them says the new number. Measured on both bitnesses, the larger limit costs nothing visible: the session starts in the same time and the core uses the same CPU and memory. The note about more processes than the audit can follow says what happened to them. It said that some of them ran with no fake clock at all. They were put on the session clock, and only the audit could not check them, which is what the note now says, on the command line and in the window. A session waiting for a library that never loads wakes the application less often. The hook looks for a window, sound or network library that the application loads after the start: often in the first two seconds, then every 250 ms for as long as the session lasts. It now backs off to once a second, so a console program that never loads those libraries is woken once a second instead of four times. A library loaded later than that is hooked up to a second after it arrives. chrono run refuses a flag given twice. A flag that takes one value (--at, --preset, --zone, --mode, --args, --cwd, --ticks, --timeout, --set-after, --jump-after, --report) used to let the last one win in silence, and so did --param with the same id. Both are now refused with exit code 1, the way chrono calc already refused them. The business-day limit counts the whole calculation. No more than 1,000,000 business days are counted in one calculation, all its steps together, where the limit used to stand on each step alone - so two hundred steps of a million business days each held the processor for seconds. The refusal names the step that goes past it, before that step is counted, and says to lower the counts. A preset has at most 64 steps and 16 parameters. The shipped presets have at most three and two. A longer file is refused by its count, like a calendar with more than 2,000 holidays, and the refusal says what to remove. "Not found" for a calendar or a preset names the folders and the ids on offer. It printed looked in /calendars as written and no id at all. It now names the folder by its path and the ids it holds, or says that neither folder exists. An unexpected exit code of the core is named. When the session closed but the core ended with a code that is not a verdict, such as 101 for a crash in the tool itself, chrono run still exits with 3 and now says which code the core ended with, as it already did for a core that stopped before the session closed. Calculator failures carry a key the window translates. A calendar that is missing or cannot be used, a scenario that is missing, broken or uses something not built yet, a value for a scenario's parameter that is missing or cannot be read, and a market calendar that cannot be loaded each end in a key of their own (calc.calendar_not_found, calc.calendar_invalid, calc.preset_not_found, calc.preset_invalid, calc.preset_not_built, calc.preset_parameter, calc.market_calendar_unavailable), so the window says them in its own language instead of in English. A preset step that cannot be used is named by its number. An unknown unit is refused with the units that work (s, m, h, d, w, mo, q, y, bd), as an unknown snap or nearest target already was. The analyser's refusals quote what was pasted. They quoted the text built from it, such as a date with T00:00:00 added, and a month out of range is now named by its number. chrono calc lists the calendars it can find in its usage, from the folder --calendar reads, where it listed three names written by hand. The window opens sooner. Its own code ships compiled ahead, so a start no longer waits for it to be compiled, and the session and result screens are built the first time a session reaches them rather than before the window first shows. Measured on the published window, to its first frame: 0.96 s to 0.80 s with the files already read once, and 1.53 s to 1.31 s on the first start after unzipping. The package grows by about 1 MB. The window package is 21 MB smaller than in 0.4.0. It no longer carries the Windows Runtime projection (Microsoft.Windows.SDK.NET.dll and WinRT.Runtime.dll, 23 MB), which nothing in it used: 168 MB to 147 MB unpacked, and about 5 MB less to download, against the 0.4.0 package. It is still built for Windows 10 1809 and Windows Server 2019 and later. The projection was the one component in the package without an SPDX licence, and the SBOM and the third-party notices no longer list it. Fixed A program no longer hangs at the start of its session, or crashes, on a thread without thread-local storage. The hook keeps a few flags per thread, and a thread can run without the storage they live in: one Windows is ending, after it freed that storage, or one started without it. A hooked wait or clock reached on such a thread read a flag through a null pointer. In one program measured that happened while Windows ended the thread that had loaded the hook, inside a heap call, and the thread then waited on its own heap lock for good: chrono run gave up after 10 s with code 2, "the target's loading is stuck". Whether it happened depended on the program and on the variables of the environment it started with. In another, a helper process crashed. On such a thread the hook now passes the call to Windows as it came, unscaled, because it cannot tell an application's call from one Windows makes inside another, and the report says so when a wait or a timer ran that way (wait.thread_without_storage). A console program that ends at once ends its session instead of vanishing. A program that ended within 300 ms of its start was taken for the second instance of a single-instance application handing its work to a copy already running, and chrono run exited with code 12 and "DID NOT TAKE EFFECT" - also for a console tool that had done its work in the session, a date printed or a commit made with the session's date in it. The suspicion now needs a windowed program among the processes of the session: a session whose processes were all console programs ends like any other, with its verdict and the program's own exit code. A windowed program that ends at once is still reported with code 12. A Rust program built on the chrono crate shows the session's time zone. It reads the zone through GetTimeZoneInformationForYear, which the session did not cover, so its local time was the session's moment in this machine's zone: a session at 03:14 +05:30 showed 22:44 +01:00. The function now answers with the session zone, asked about the current zone or about the zone the session names "Chrono Session", and the report lists it among the covered zone channels. A Rust program built on the jiff crate shows the session's time zone. It finds a zone by its name, and the session zone has a name no table holds, so it showed its time in UTC under every session. Such a program is recognised in its file, and the session gives it the zone in the TZ variable in POSIX form, CMS-5:30 for +05:30. A TZ of your own is replaced for it, and the core says so on its error output. No other program gets the variable, because Node.js reads it too and turns that form into an unknown zone at +00:00. A jiff program started by another program of the session is not recognised and still shows UTC. The support matrix says what is measured, and by what. It adds the runtimes the release tests have been measuring all along - Node.js and Deno, Ruby, Rust, and Go, whose wall clock no hook reaches - and says which rows the embedded-engine stand and the runtime maps measure rather than a person. The window takes a shortcut as well as a program, and the batch script row now says so. The honest limits name a limit measured on the way: a TZ variable of your own wins over the session zone in Python, MSYS2 and Node.js, and the report does not say so yet. More than one file is refused for the count. Two programs dropped on the window at once, or given on its command line, are answered "One application at a time". They used to get the text for a file that is not a program. The calendar beside a date works from the keyboard. Opening it puts the focus on the day of the date in the field, in that date's month - it used to open on the current month whatever the field held, with the focus left on the button that opened it. The arrows move the day, Enter or Space takes it, Escape closes the calendar and puts back what the field held, and the focus returns to the button. The first arrow used to close the calendar and write a date the reader had not chosen, counted from today. A history that could not be saved is on the screen. When the history file could not be written - a session's record, a removal or a clear - the history section now opens and shows the reason. The section is folded, and its summary says only how many sessions it holds, so the reason stood behind the fold, and a clear or the removal of the last session that failed took the whole section, reason and all, off the screen. The focus stays on the screen when the control holding it goes away. Removing a calculator step with its own button puts the focus on the step now in its place, else the one before it, else Add step. Format rows an error takes off, and a Copy as command or Use this date an error switches off, hand it to the custom format. Each of them used to drop the focus on the window or the column, where the next Tab started again from the top. A screen reader names every control the keyboard reaches. The calculator's start point, time zone, step type, jump and nearest-day pickers, both search fields, the header of every folding section, and the time zone, speed, arguments and working folder fields were read as an unnamed "combo box" or "edit". The calculator's copy buttons are named after their format ("Copy ISO date"), its steps by number ("Step 2", "Remove step 2"), and the rows of the session history by everything they show, where two sessions of one program used to read the same. Tab no longer stops on nothing. Form rows, the session clocks, section summaries and lists of text each took a Tab stop with nothing to do there, which a screen reader could only call the window - four extra stops on the setup screen and five on the calculator. The window no longer waits while the engine or the core starts. The calculator starts the engine for every answer, and the window's own thread used to do the starting, so every edit paused it for as long as Windows took to start a program - longer on a machine whose antivirus scans the engine first. The engine and, at Start, the core are now started off the window's thread. Arrowing down the session's scenario list started the engine for every row it passed. A row chosen while the one before it is still being worked out now waits a fifth of a second, whether it was clicked or reached with the arrow keys, so a run of rows asks about the row it stops on. A row chosen when nothing is being worked out, such as the first click on the list, still asks at once. A large process family no longer holds the window as its reports come in. Every change on the session panel told all nineteen of its commands to check themselves again, and every coverage report rebuilt the audit lists even when it said nothing new. A family of 256 processes reporting the same channels took 275 ms of the window's time and now takes 37 ms. A heartbeat no longer re-announces the whole result either. The copied session summary says what the CLI report says about errors, sleep and the browser. Every error the core sent is listed under the verdict - a speed it refused while the session ran was on the screen only while the session lasted. How long the computer slept is in the session's block, and the result screen shows it beside the elapsed times. The browser a Chromium session drove is named under the application. The sleep warning is on the session's warning list as soon as the core notices the sleep, not only once the session ends. The diagnostics block says which window, system and core a session ran with, and what failed. It names the window's version, Windows, the core's version, bitness and path, whether the session drove an application or a Chromium browser, and that browser, and it carries the whole exception the window caught, with its stack. Every line of the core's output has its time since the session began. A core that could not be started at all now leaves a block too - it used to leave none. A failed start writes the whole fault to the diagnostics folder and its box says where. The box showed the exception's message and nothing was written. Two different unexpected faults thrown from the same place inside .NET are no longer taken for one, and faults past the eighth, which are no longer shown, are still written - up to 32 different ones a run, the last saying so. A history this version cannot read is set aside with a word, and a killed write leaves no file for good. The history panel says where the earlier history was moved when this version could not read it, instead of starting again with one row and saying nothing. A scratch file left by a write that was killed half-way is removed by the next write once it is ten minutes old. A history that was set aside is never removed. "New session" comes back at the mode the last session started with. Changing the speed while a session ran moved the Mode list with it, so a session started in flow left the next form at x60. The "Jump to" field of a new session is empty. It kept the moment typed in the last session, ready to jump the new one there. A calculator answer counted from "Today" or "Now" is brought up to date before it is used. It was computed once, so a "Today + 30 days" left open overnight went to the substitution panel and the clipboard as yesterday's answer. Showing the calculator again, "Use this date" and Copy now ask again when the day (Today) or the second (Now) has moved on, and wait for the new answer. A calculator scenario that counts in a calendar this installation does not have is no longer computed in another one. The window kept the calendar picked before, so a scenario for a market with no calendar here counted its business days in another country's, without a word. It now says which calendar the scenario needs and computes nothing. When the list of calendars cannot be read, the calendar field says why and offers no calendar. A timer or a delay whose absolute due the session's clock has already passed fires at once. SetWaitableTimer, SetWaitableTimerEx, SetThreadpoolTimer and SetThreadpoolTimerEx got the shortest relative due for it, which Windows fires only at the next tick of the system timer - up to 15.6 ms of real time at the default timer resolution, 15.6 s of session time at x1000. An absolute NtDelayExecution deadline already reached waited for the same tick. Each now answers as it does without a session: the timers fire at once, and NtDelayExecution returns at once with the status of a deadline already past rather than the one for giving up the processor. The WebView2 policy value of --elevated-embedded is always its own session's, also when the * value holds a session marker. A session copies the * value into its own and puts its marker last, but the first marker was read. So a * value carrying one - a tester who copied a session's value there - made the session's value look like another core's: the session left it in the machine registry while saying it was removed, and another session's look for leftovers removed it while its session ran. A broken marker in * hid a dead session's value from every look after it. The last marker is read now. A look for leftover policy values never removes * or the key's default value. No session writes under either name, but a * value carrying the marker of a session that is gone was taken for that session's and removed. A preset or calendar file is refused in the words of the file, not of the program reading it. A rule type or a step name the reader does not know was an "unknown variant", and a rule type that is not text "expected variant identifier" - the names those things have in Rust. They are now an "unknown name" and a "name, written as text". A preset step that is not an object ("steps": [5]) or names two steps at once was refused with "expected value", the sentence for a file that is not JSON, so its author looked for a missing comma. It is now refused as what it is, with the step names it may use, and a second key is named. A date that is not one is quoted as it was written. A preset parameter's default, or a --param value, that is not a date (2026-13-01) was refused quoting '2026-13-01T00:00:00', a text nobody wrote. The refusal quotes the date as given. chrono presets names the step of a preset that does not resolve. --preset has said "step 2:" for a step it cannot use, and the catalogue - the list the window shows its skipped files from - gave the same reason without the number. Both say it now. A protocol line past the size limit is called too long, whatever character the limit cuts. The core reads a command line of at most 1 MiB. A longer line whose limit fell inside a character of two or more bytes was refused as protocol.stream_unreadable ("not UTF-8 text"), which sent a client looking at its encoding when the length was what it got wrong. It is now protocol.line_too_long, as for any other line past the limit. A pasted date with a sign in a number is refused. chrono calc --analyze "+1/1/2026" read the date as January 1st, because the integer reader takes a leading +. A US or Polish date is now digits only in all three numbers, the rule an ISO date's fields have had since the analyser refused 2026-+1-05, and the refusal says so ("bad number ... (digits only)"). A Chromium session that could not start says what it left behind. When the browser never opened its debugging port, the session ended only the process it had started, tried once to remove the profile and reported clean whatever happened. It now ends everything the browser started and removes the profile the way the end of a session does, and a profile it cannot remove is named (cleanup.chromium_profile_left). A process that refuses to be opened counts as running. One that runs as administrator or as another user used to read as ended: the clean-up of old Chromium profiles could remove the profile of a live session, and a session took such a member of its application for closed. It now counts as running, and the session says session.left_running for it. Looking for web engines inside an application goes on after a failed read. One table of listening sockets that could not be read ended the search for the whole session, and took the other table with it. The two are read apart now and a failed read is tried again. The search gives up, and says so (embedded.discovery_unavailable), only when the IPv4 table, the one web engines open their debugging port on, could not be read several times in a row. A Chromium session reaches the first window sooner. It looks for the browser's debugging port every 10 ms instead of every 150 ms and connects by the address the browser writes beside the port, so the first window of an application is paused for the time shim more often. Pages and workers the session could not reach are counted and named. One the session could not attach to was skipped without a word, and why a time shim failed was dropped - both now count against the verdict, and the first three are named on the diagnostic stream. A connection that closes while the browser still runs says why. A Chromium session lets go of what it does not drive. A second debugging session on a page, a page the native mechanism covers and a target with no JavaScript are detached from instead of being kept for the whole session, and the session remembers a bounded number of the workers that came and went. A process of the session that cannot be watched is named. The session lasts while a process on its clock runs, and one that would not let Chrono Mock wait on it (access denied) used to count as ended, so the session could end while it still ran, without a word. The report now says so (session.member_unwatchable): from the end of the session such a process runs on the real clock. Start-up refusals say what happened. A program that asks to run as administrator, a path or command line that is too long, a program built for another processor, a file antivirus blocked or removed and a program a system policy blocks are named instead of shown as a bare number. A session block left by an application that another version of Chrono Mock started is told from one that does not belong to Chrono Mock at all, and says to close that version's applications. When the hook library will not load in the application, the report says whether Chrono Mock itself cannot open it or it is built for another machine. The error numbers stay in every message, and none of them is the system's own text, which came in the system's language. A program the application starts under its own debugger no longer stalls the start for ten seconds or reads the real clock. When the application under test started a program with DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS (a launcher that debugs what it starts, say), Chrono Mock waited in the application's own thread for the hook to load in that program, while the program waited for that very thread to answer its first debug events. The start stood ten seconds, the program then began before the hook was in place and read the real clock, and the report named it uncovered although the hook joined it a moment later. The wait now happens in a thread of its own, the call returns at once, and the program starts once the hook is in place. A debugger that asks for the program suspended and resumes it itself can still start it before the hook is in. A child process that Windows could not start is no longer reported as running on the real clock. When the application under test started a program that Windows ended while loading it, before the program ran (a library it needs is missing, say), the report listed that program among the processes the hook could not follow, said it ran on the real clock and gave the session a partial verdict. It never ran, so it is left out now, the way Chrono Mock already read its own target. On 32-bit Windows with --scale-qpc, the precise system clocks no longer follow the scaled counter. Windows computes them through the exported QueryPerformanceCounter there, so after the session the application's precise wall clock stayed ahead of the real one by everything the counter had gained (200 s after a 3 s session at x60, measured), and its precise interrupt time ran off during the session too. 64-bit Windows was not affected. An absolute NtDelayExecution under --scale-duration waits for the fake date and time. It was passed to the kernel as it was, which compares it with the real clock. A delay shorter than a millisecond is no longer lengthened. NtDelayExecution with 100 ns, a common way to give up the processor, was held at 1 ms at every speed, x1 included, and reported as a wait the speed could not divide. The fake elapsed time no longer turns negative after about 10.7 days at the highest speed. It stands at its limit and time.duration_axis_clamped says so, which it now also does when the limit is reached over several speed changes. A core stopped in the middle of a speed change costs a target one wait, not one per clock call. The 2038 marker no longer disappears after the year 30828. It was counted through FILETIME, which ends there, while the calculator reaches the year 262143. Business days on a calendar with many holidays, and long format masks, take less time. A day off is now found by binary search: +200000bd on a calendar of 2,000 holidays went from 1.3 s to 0.86 s, measured. A format mask full of letters that are not tokens was checked in quadratic time: 30,000 of them went from 0.85 s to 0.06 s. A flag that takes a value no longer swallows the flag after it. chrono run app.exe --report --dry-run ran the application and wrote the evidence into a file named --dry-run instead of printing the plan, and --report --no-embedded dropped the opt-out. A word that starts with two dashes is now refused as the value of a flag, naming both. --args still takes flags, because they are the application's own, and a path that starts with two dashes can be written .\--name. A window, sound or network library present at the start can no longer take the hook with it. The hook keeps such a library loaded for as long as the application runs, as it already did for one loaded later, so an application that frees it cannot leave the report claiming a function whose hook went away with the library. Security A calendar or preset file cannot hide text from the terminal. A text or key in such a file that holds a control character, a format character (the ones that reverse the direction of text or have no width) or a line separator is now refused when the file is read, naming the place in the file, the position and the character. A preset whose description held a carriage return and an escape sequence printed a fake result line over the real one. These files are made to be shared, and none of the shipped ones holds such a character. A refusal that quotes such a file where the check cannot read all of it - a file that is not valid JSON, or one with a key given twice - shows the character as its code. The same characters in what an application or a browser says about itself - a process name, an error, a refused connection - are now shown escaped. --report cannot overwrite a program, and writes its file whole. The path is checked before the session starts, also in a dry run: a folder that does not exist, a folder, the application itself and any other program file are refused with exit code 1. The evidence is written to a temporary file beside the target name and then renamed over it, so a failure leaves no half-written file, and when the old file cannot be replaced the message names the temporary one that holds the evidence. The temporary file is always created new, never written through one that is already there - a temporary file an earlier run left with its evidence, or a link. A core in the window's package reads only the package's calendars and presets. Run on its own from core\x64 or core\x86, it found no catalogue beside it and read the one in the current folder, so a calendar placed in a shared folder could answer business-day questions in a report. It now reads the package's own folders and never the current folder's. The window was not affected. A catalogue file is read with a bound. Its size and its contents come through one open file, and the read stops past the 4 MiB limit even when the file grows while it is read. The window package carries .NET 10.0.14. It is the .NET security release of 2026-10-25, after the one of 2026-10-13 (10.0.13), and the window, which brings its own runtime, bundles it in place of 10.0.12. The command line does not run on .NET and is unchanged.