Soak run procedure

This is the working procedure for the PBI-112 device observation, which is the two-hour continuous run of the firmware on the physical classic LILYGO T-Display. It covers the flash, the capture, what counts as a pass, what counts as a failure, and the cases to check while the run is going.

The observation is maintainer evidence under section 10.6 of the active architecture. The transcript is half of it and the maintainer’s own words are the other half, so the run is watched rather than only recorded.

Before starting

The board is the classic variant on /dev/ttyUSB0, and the image is the one built from the current tree. Two hours is the stated duration rather than a judgement, and a shorter run is not the same evidence. The run is not reset, reflashed, or power cycled while it lasts, so the desk it sits on should be one nobody needs for two hours.

Activate the ESP-IDF installation first, because flash and monitor both delegate to idf.py.

source ~/.local/opt/espressif/export.sh

Flash

cd platforms/esp_idf && idf.py build
cd ../.. && ./pet.py flash

flash names an inactive SDK environment or an absent port before it writes anything, so a refusal at this stage is a setup problem rather than a run failure. Add --port /dev/ttyUSB0 if another serial device is attached.

Capture

monitor needs an interactive terminal, because ESP-IDF requires a TTY on standard input. Keep the whole transcript, because the first heap line and the last one are the two readings the observation is recorded from.

script -q -c "./pet.py monitor" _report/soak-transcript.txt

_report is ignored by Git, so the transcript stays out of commits. The monitor exits with Ctrl-C for runs started from platforms/esp_idf, and the script wrapper exits with it too. Note the wall clock time the run started and the time it ended.

What a healthy run looks like

The transcript opens with the boot, the panel, and the readiness lines, then carries one heap line about every minute and nothing else.

pet event=boot board=t-display-classic target=esp32 version=0.4.0
pet event=display-ready width=240 height=135 rotation=90 colour-order=rgb
pet event=runtime-ready tick-ms=40
pet event=heap free-bytes=142312 min-free-bytes=139880 uptime-s=0
pet event=heap free-bytes=142312 min-free-bytes=139880 uptime-s=72

The heap reading is emitted every 1800 iterations rather than on a timer, so the difference between two uptime-s values is the measured pace of the 1800 iterations between them. About 72 seconds is a 40 millisecond iteration. A longer gap is a slower loop and is worth recording as the pace it actually is.

The panel shows the pet rather than a pattern. The presentation is a calm shape on a canvas that fills the height of the panel, with a letterbox band at each side, and the body changes tone and size when the activity or the expression changes. Without a button the world reaches curious about a minute after boot and stays there, so exactly one change is expected on the glass for the whole run.

Pass and fail

The run passes when it reaches two hours on the board, the transcript shows no unexpected reset and no watchdog abort, the minimum free heap does not fall continuously across the run, and the loop is still reporting its readings at the end.

The run fails when the board resets or aborts on its own, when the readings stop arriving while the board is still powered, when min-free-bytes falls step after step across the whole run, or when a runtime-fault line appears and does not clear. A run that ends early is reported with the time it reached and the reason rather than repeated quietly.

A single time-clamped line is not a failure by itself, and it is worth recording, because the 1000 millisecond bound was chosen against a 40 millisecond iteration and nothing ordinary should reach it.

Any defect found here is filed in bugs rather than fixed inside PBI-112.

Cases to check

Each row is one step and what that step should produce. A row whose expected value does not appear is recorded with what appeared instead.

board-flashes-and-boots

StepExpected
Run ./pet.py flashThe image is written and verified, and the tool reports no inactive SDK environment and no absent port
Start the monitor and watch the first secondsOne boot line naming t-display-classic, esp32, and the project version

panel-comes-up

StepExpected
Read the line after bootpet event=display-ready width=240 height=135 rotation=90 colour-order=rgb
Look at the glassThe pet is visible, which is a lighter body centred on a canvas that fills the height of the panel, with a letterbox band at each side
Search for display-failedNothing. A line here names the stage that refused and the run continues without the panel

runtime-ready-arrives-once

StepExpected
Search the whole transcript for runtime-readyExactly one line for the run
Read its fieldstick-ms=40
Check where it sitsBefore every heap line and after display-ready

first-heap-reading-opens-the-run

StepExpected
Read the first heap lineIt carries uptime-s=0 and arrives immediately after runtime-ready
Write down its free-bytes and min-free-bytesTwo values to compare the end of the run against

heap-readings-keep-arriving

StepExpected
Watch the transcript for the first five minutesA new heap line about every 72 seconds
Check again every twenty minutes or soReadings still arriving, with uptime-s increasing each time
Check once more just before stopping the monitorThe last reading is no more than about 72 seconds old

iteration-pace-holds

StepExpected
Subtract the uptime-s of one early heap line from the nextAbout 72 seconds, which is a 40 millisecond iteration over 1800 of them
Repeat in the middle of the runThe same difference
Repeat for the last two readingsThe same difference, and record whatever the value actually is

minimum-free-heap-does-not-fall

StepExpected
Put the first min-free-bytes and the last one side by sideThe two values are the same or close, and the difference is recorded either way
Scan the readings between themThe value settles early and stays there
Look for a step down that repeatsNothing. A drop every minute is a leak, and a single early drop as the bring-up runs is not

free-heap-stays-steady

StepExpected
Compare free-bytes across the readingsIt stays about the same across the run
Compare the first reading with the lastNo steady decline, which would be a leak min-free-bytes has not caught up with yet

no-unexpected-reset

StepExpected
Search the transcript for bootOne line for the whole run
Search for rst: and Guru MeditationNothing. Both are SDK output that a reset or a panic produces

no-watchdog-abort

StepExpected
Search for watchdog, WDT, and task_wdtNothing. The loop yields every iteration, so the idle task feeds its watchdog

app-main-does-not-return

StepExpected
Search for Returned from app_main()Nothing. The SDK prints it when the entry returns, which the run loop never does

no-standing-fault

StepExpected
Search for runtime-faultNothing in a healthy run
If a line appears, look for a second one after itA fault is reported once when it appears and again once it clears, so a line with no partner means the condition never cleared

time-is-not-clamped

StepExpected
Search for time-clampedNothing in a healthy run
If a line appears, record its elapsed-msThe value says how much an iteration lost, and anything reaching the 1000 millisecond bound is worth reporting

transcript-lines-follow-the-format

StepExpected
Scan the project’s own linesEvery one opens with pet event= and carries lower case hyphenated keys
Look for free-form status text among themNothing. A line that does not follow the format is not release evidence

run-reaches-two-hours

StepExpected
Leave the board running without resetting, reflashing, or power cycling itThe run continues untouched for two hours
Stop the monitor after two hours and read the last heap lineAbout uptime-s=7200, with the readings still arriving when the monitor was stopped
Note the wall clock start and end timesA duration that matches the uptime the transcript reports

What to record

The observation needs the board and the variant, the firmware commit, the date, the duration reached, the first and last min-free-bytes side by side, the pace the uptime-s differences imply, and anything unexpected the run produced. The transcript is kept with it.

The run that was made

The run was made on 2026-08-07 and it passed. It started at 19:04 and the monitor was stopped just after 21:08, and the last complete reading was uptime-s=7416, which is 2 hours 3 minutes 36 seconds. The board was the classic variant on /dev/ttyUSB0 and the image was the working tree over commit 6c6e1e5. The transcript reports version=0.4.0 because that is the project version until v0.5.0 is released, so the observation names the commit rather than the version.

All thirteen cases produced their expected value. The transcript carries 104 complete heap lines and one boot, one display-ready, and one runtime-ready line, and no runtime-fault, time-clamped, display-failed, Guru Meditation, watchdog, or Returned from app_main() output. The one rst: line is the POWERON_RESET the run started from. free-bytes and min-free-bytes were both 237104 at the first reading and at the last, unchanged through every reading between them, and all 103 gaps were exactly 72 seconds, which is the announced 40 millisecond iteration over 1800 of them. The maintainer observed the pet on the glass across the run, changing once about a minute after boot as the world reached curious and holding after that, which is the whole of the progress that is available before PBI-125 maps a button to a greet.

The transcript is kept at _report/soak-transcript.txt and goes to the release evidence with the observation when v0.5.0 is prepared. PBI-112 and PBI-116 are marked, E32 and E33 are closed, the progress rows carry the readings, and LIM-012 is narrowed to what stays unverified after a continuous run.

Repeating it

This file stays the procedure rather than becoming a record, because the release will want the run repeated against the tagged image and PBI-128 asks for device evidence again.

When a run fails, the defect is filed in bugs with the transcript around it, the observation is not recorded as evidence, and the fix is its own task rather than part of it.

When a run ends early, the time it reached and the reason are recorded, and it is repeated from the start rather than continued, because two hours is the stated duration.