Stopping a recording with a name that a macro already has (in any case) saved over that macro's
steps, inputs and description, with only Undo to get them back. A taken name now gets a suffix
("Tidy (2)"), and the save passes replace: false as a backstop.
Stopping a recording converted and saved it against the active workbook. For example, starting a
recording in Book1 and switching to Book2 before stopping saved the macro into Book2, inferred the
input's header and column types from Book2's cells and looked up inlined macros and scripts
there. The recording is now converted and saved against the workbook where it started, and the
active workbook stays active. If that workbook was closed, stopping returns an error and ends the
recording.
An Automate script run while recording was replaced by all of its steps except automate.* ones,
without the policy check that top-level commands get. For example, a script that runs
window.activate or macro.list produced a recorded macro that the runner refused on playback. The
steps of a recorded script run, and of an inlined macro run, now go through the same policy as
top-level commands; refused ones are left out and listed as skipped.
A macro run while recording was replaced by every command in it, including both branches of an
If and the body of a Repeat, whenever it had no inputs. For example, recording a run of a macro
with an If played back both the then and the else commands without a condition, and commands
using Row, Column or Output failed on playback. The run is now inlined only when the macro has no
inputs, no output and only plain commands (its disabled steps are left out); otherwise it is left
out and listed as skipped.
The recorder made every unqualified reference relative to the example input, whatever sheet the
step ran on. For example, starting a recording on Sheet1 with B2:C5 selected, activating Sheet2
and setting C4 recorded Input.cell(3,2), so playback wrote Sheet1 instead; a formula entered on
Sheet2 was changed to refer to Sheet1's cells. Each step now keeps the sheet it ran on, and only
references on the input's sheet become tokens.
Stopping a recording took the recorded commands and where recording started before saving the
macro, so a failed save threw both away. For example, stopping with a name that holds a control
character, or in a workbook at its macro limit, ended the recording, and stopping again with a
good name found nothing to save. A failed save now puts the recording back, so stopping again
saves what was recorded.
Session::execute checked the debugger lock only for top-level undoable commands. A command that is not undoable could run edits as nested commands and skip it: stopping Record Actions while a macro in the same workbook was being debugged saved the script inside the run's undo group, and stopping the run without keeping its changes deleted the script again. The lock now applies at every depth, and only the debugged run's own steps go ahead.
The intro said a macro run rolls everything back on an error, but a step that exports or prints to a path writes the file before a later step can fail, and the Running section says that file stays. The intro now limits the guarantee to workbook changes.
The policy summary said file commands never run in a macro, but File >
Properties runs, and exports and printing run: without a path they only
produce bytes, and with one they need the permission and approval. The
summary now names what is denied and what runs, and the paragraph is
wrapped to the file's line length.
The policy denies window.* commands, but the View tab's window commands
are view.*: a macro could run view.newWindow, which adds a window that
the run's rollback doesn't remove, or view.hideWindow and
view.unhideWindow, which change the session's hidden windows.
The policy now denies those three. Arrange All changes nothing in the
engine and Switch Windows only lists windows, so both stay allowed.
footprint always ran its dry run with the default 60 second limit, so
macro.run with timeLimit 5 could run for over two minutes (a dry run,
then another after a shift) and fail with "ran longer than 60 seconds".
footprint now takes the run's clock and limit. make_way starts the
run's clock before the dry runs, so the checks and the run share one
limit, and macro.check takes timeLimit too.
The footprint dry run stops at an approved file step and at Solver, but
make_way took a partial footprint with no conflicts as proof that the
run was safe, also after a shift. A macro that exported a CSV and then
set B1 ran under onConflict fail or ask and overwrote B1 without asking.
When the footprint is partial and the rule is not overwrite, the run's
machine now checks every step as it runs: a step that writes over
cells, sheets or objects that held data when the run started stops the
run and rolls it back, with a confirmation under ask and an error
otherwise. make_way now returns the machine for the run along with
whether it shifted.
`Name:` read a name only up to the first period, so `Name:Sales.Q1` looked up `Sales` and
treated `Q1` as a method, and a workbook name called Sales.Q1 could not be used in a macro,
alone or as `Name:Sales.Q1.cell(1,1)`.
The name now runs through periods, and the longest defined name or table that exists and ends
at a period (or the end) is used; what follows is read as methods. So `Name:Data.row` is a
name called Data.row, and `Name:Sales.offset(1,0)` still moves Sales. When no name matches,
the error names the text before the first method, and a bad method after a dotted name is
still refused. Tests cover direct resolution, reference parameters, `$ref` and `{{...}}`,
with and without a chained method.
col("...") always matched against the first row of the range, whatever
its header flag. On an input declared without a header row,
Input.col("Amount") silently picked the first column whose first data
cell read "Amount". After .data, the natural Input.data.col("Amount")
failed, because the first row was then a data row. The header row is
now carried through data, and col("...") is refused with a message when
there is none (an input without a header, a Name: base, or after any
other method).
is_token looked only at the leading identifier, so on a workbook with a
sheet called Output (or Row, Column, or the name of a declared input),
"Output!A1:B2" was read as the Output token, and the step failed with
"the macro has no output anchor" or "unexpected '!A1:B2'". This hit
reference keys, $ref and {{...}} alike, including references the
recorder writes for other sheets. A base followed by '!' is now a sheet
name, and the text resolves as a plain reference.
Opening a workbook accepted a macro whose format version `v` was above MACRO_VERSION. Serde
dropped the fields this build doesn't know, and the next save wrote the reduced macro back
with `v: 2`, so a newer build would read it as a complete version 2 macro.
A version above MACRO_VERSION is now refused by Macro::check_limits, and parse_macro checks
the raw `v` before reading the steps. Such a macro takes the existing per-macro path: it is
left out with the usual single warning (which now names a newer format), and the rest of the
workbook opens. Tests cover JSON and xlsx hidden names, with current and missing versions as
controls.