Skip to content

v0.16.0

Choose a tag to compare

@AminChirazi AminChirazi released this 28 Aug 11:14
· 153 commits to main since this release
6c3eef3

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_dispatch only, so pushing v0.15.0 did 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 than v* 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: a v0.14.0 tag 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 quote was 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 leaves readyState complete at 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 bakes step_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 its FLOWPROOF_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 main for a day, and nothing on a pull
    request could see it.
    web E2E and windows build + E2E are 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_e2e builds a FlowSpec literally and the struct had grown
    three fields it did not list (apps, exports, login). The file is
    #![cfg(windows)], so no amount of cargo clippy --all-targets on Linux or
    macOS ever compiled it.

    web_e2e asserted the whole-run GIF renders, but GIF assembly became opt-in
    when --video landed 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.