v0.16.0
Recording could not finish a flow written the way a tester talks. This
release is the seven-defect chain that stopped it, found by recording one
insurance quote against a live application until it worked.
Every one of them was a fault in what the recorder could PERCEIVE, and
every one of them read, at first, as the authoring model being wrong. It
never was. The scene described a page that had moved on, or omitted a
control the page had drawn for itself, or reported a rejected form as a
clean one — and the model reasoned correctly from what it was handed.
The flow this was found on now records end to end and replays
deterministically, six consecutive runs, zero model calls.
Changed
-
A version tag now publishes. Both publish workflows were
workflow_dispatchonly, so pushingv0.15.0did nothing and the release
still had to be triggered by hand — a step easy to forget after tagging, and
easy to mistake for having released.They now also fire on a tag matching
v[0-9]+.[0-9]+.[0-9]+. The pattern is
exact rather thanv*so an experimental tag cannot ship a release, and a
new guard refuses a tag whose name disagrees with the version it would
build. That guard is not hypothetical: av0.14.0tag was created on the
commit that bumps to 0.15.0. While nothing consumed tags it was merely wrong
in the history; with tags publishing, the same slip would ship 0.15.0
labelled 0.14.0, and registry versions are immutable.
Fixed
-
The authoring scene could describe a page that no longer existed, and the
model took the blame. The inventory was read the instant the previous step
finished. A step that navigates leaves the next step's scene racing the new
page, and a server that renders every variant of a form and hides the
irrelevant ones in script briefly presents all of them as rendered.On the Tricentis sample app this was reliable rather than rare.
Open the truck insurance quotewas authored correctly, and the scene captured
immediately after was the union of two forms: a motorcycle#model—
actionable, with its own options list — beside the truck's#payload. The
model grounded faithfully onto#model, exactly as instructed, and the
recorder then refused the step because no such element existed by the time
it ran. The failure read as a model inventing a selector. It was the scene
that lied; the model had copied a target it was entitled to copy.The web scene is now read only once two readings agree on which targets
exist, with the document loaded. That narrows the window rather than
closing it — a navigation that has not begun still presents a settled old
page, and a single-page app never leavesreadyState completeat all — but
it removes the case this was losing recordings to.
Agreement before load completes does not count, because that is precisely
the window the pre-script union looks stable in. Only the set of targets has
to hold still — a clock, a character counter, or a field mid-edit changes
text on every reading without changing which elements exist, and waiting on
those would wait forever. A page that never goes quiet is captured anyway
after a bounded wait: recording a carousel is better than hanging on one. -
Recording refused a click replay would have waited for. The occlusion
probe's own comment said it used "the same predicate as replay's
actionability gate, so the two agree by construction". The predicate was the
same. The waiting was not: replay polls the gate to the step's deadline,
recording asked once and aborted. A toast on its way out, a modal backdrop
mid-fade, a carousel between slides — each reports "something else would
receive this click" the instant it is asked, and each is gone a moment
later. Recording refused pages it could have recorded, and the trace it
declined to write would have replayed.Recording now polls the same gate for the same budget, measured as wall
clock exactly as replay measures it — a count of polls would omit the probe
round-trips between them and let recording out-wait replay on a slow
transport. Not a new constant:
recording bakesstep_timeout_ms()into every targeted step's existence
precondition and replay waits exactly that recorded value, so both are
derived from the one number and follow itsFLOWPROOF_STEP_TIMEOUT_MS
override together. The direction is the part that matters — recording must
never accept a click replay would reject, so out-waiting replay is the one
error that cannot be tolerated: it mints a trace whose first replay fails.An occluder that outlasts the budget is still refused, with the message it
always had. This buys the transient cases only, which is all a wait can buy. -
The two E2E jobs had been red on
mainfor a day, and nothing on a pull
request could see it.web E2Eandwindows build + E2Eare off the PR
path, so they run only on push to main — every PR involved went green, and
main went red on merge, repeatedly, for two unrelated reasons.notepad_author_e2ebuilds aFlowSpecliterally and the struct had grown
three fields it did not list (apps,exports,login). The file is
#![cfg(windows)], so no amount ofcargo clippy --all-targetson Linux or
macOS ever compiled it.web_e2easserted the whole-run GIF renders, but GIF assembly became opt-in
when--videolanded and the test kept running with defaults — it expected
an artifact nothing had been told to produce. It now asks for one, so the
assertion tests what it claims to.