Skip to content

v0.6.2

Choose a tag to compare

@mangokingTW mangokingTW released this 05 Sep 12:51
· 17 commits to main since this release
aee12e4

Closes the remaining items of #93 (C5, C6, C8, C10, C11): a failure now says
which failure it is, a death names its cause, the sweep's hides are accounted for
and undone, launched children cannot hold the step's pipes, and a failed session
leaves the frames at the moment it failed.

Added

  • Failure signatures. WintegrateError keeps the facts passed at raise time
    and builds signature from the class's SIGNATURE_KEYS allow-list --
    FocusStealDetectedError[foreground_image=explorer.exe],
    WindowDiscoveryTimeoutError[process_names=notepad.exe] -- so two failures with
    the same count stop reading as the same problem. No hwnd, pid or timestamp may
    be a key: one per-run integer makes every signature unique. The foreground is
    sampled during the focus retry loop, not at raise time, and carried on
    TextMismatchError too, the raise a mid-typing steal most often reaches.
    step_failed, session_error, the job summary and READ_THIS_FIRST.md print
    it.
  • ConsoleHostEndedError. When the console this process shares is destroyed
    during Session.__enter__, what reaches the traceback is a KeyboardInterrupt
    at whatever line was executing. __enter__ now re-labels it, on evidence only:
    the console probes that answered at preflight answer nothing now. The message
    names the phase it was in, how long ago it entered it, the processes the sweep
    ended and the console peers that die with it. Still a KeyboardInterrupt, so a
    pytest run aborts once. A real Ctrl-C with the console intact is untouched. The
    verdict is journaled (console_host_ended) before it is raised.
  • step_failed now carries the same window census delta as step_ok: the
    windows that came and went during the failing step were computed and thrown
    away on the only path that matters.
  • frames/ at the failure. A failed session now pulls the recording's frames
    at the moments the timeline names into frames/, each named by video time and
    event (t041830ms_step-failed_submit.png), with frames/index.json mapping
    files to events. The budget is small (8) and the priority explicit: the window
    around the last step_failed first, the tail of the recording second, the rest
    after -- and marks that do not fit are dropped whole and listed as dropped,
    because a silently truncated set reads as the whole story. READ_THIS_FIRST.md
    says the directory is there. extract_frames, plan_marks and video_ms_for
    are public, for pulling frames from any recording with its anchor.
  • expect_artifact(path, timeout), replacing a sleep after launching
    something that writes a file: waits for the file to exist, be non-empty and
    stop growing, and on the deadline raises ArtifactMissingError naming what the
    directory actually held -- the .tmp that was left behind, or nothing.

Changed

  • The sweep's hides are recorded, verified and undone. sanitize_ci_runner_environment()
    used to run three ShowWindow(SW_HIDE) calls that recorded nothing, verified nothing
    and restored nothing -- on a library whose charter names focus stealing as the
    enemy. Each window hidden is now an InterventionResult (what was intended, what
    a re-measurement found 0.3 s later, whether they agree) on KillPlan.interventions,
    journalled as hide_result and written into kill_plan.json; the foreground is
    measured before and after the pass. Session.__exit__ shows again what this
    session hid and that is still hidden and still exists -- nothing else -- and
    records that too (restore_result). The rule (hide_reason) and the restore
    selection (restore_targets) are pure functions with offline tests.
  • Launched children no longer inherit this process's stdio. Window.launch_and_discover
    gave every child the step's stdout/stderr handles; an orphan holding those pipes is
    what kept a CI step in_progress after the process that started it had died. Inside
    a Session a child's output goes to launched_NN.out / .err in the artifact
    directory and a discovery timeout quotes it -- a child that printed an error and
    exited is the usual reason no window appeared. Outside a session, DEVNULL. stdin is
    DEVNULL either way. READ_THIS_FIRST.md explains both when the files are present.

Full changelog: https://github.com/mangokingTW/wintegrate/blob/main/CHANGELOG.md