Closed bug report

Wake-from-sleep reclaim writes the literal string "reclaimed" into owner, renders it as a taskbar thread tile, and permanently poisons hwnd resolution for every later session (answers #70; pup_raise_os_window then lies ok:true)

John Lauer · 2mo ago ·closed by John Lauer

A wake-from-sleep reclaim pass writes the literal string reclaimed into owner, and it never goes away

pup-bridge 2.0.353, ab 2.0.80 (1f66e72e), Windows, AdomLapper, tiles mode.

This is the answer to the open question in #70 ("Not yet established: whether the owner is (a) never persisted, (b) persisted but not restored, or (c) restored and then cleared by a later adoption/reclaim pass"). It is (c), and it is worse than a clear: the reclaim path writes a sentinel string into owner, that sentinel is rendered on the taskbar as the thread name, and the poisoned session then degrades every session created afterwards for the rest of the pup process's life.

1. What the human sees

John's laptop slept overnight. In the morning his taskbar carries a pup thread tile whose label is the word reclaimed, wrapped across two lines as recl / aime. His words: "it looks like some kind of weird 'reclaimed' pup window that pup created, which is horrific."

It is not a category downgrade like #70. It is a tile confidently rendering an internal placeholder as if it were an AI thread's name.

2. Ground truth from pup_list_windows

Verbatim, the reclaimed session next to three healthy ones in the same process:

{ "sessionId": "adom-you-win05jo", "ageMinutes": 143,
  "owner": "reclaimed",          "ownerSource": "declared",
  "overlay": "favicon+adom:wiki.adom.inc",
  "profile": "adom-you", "tabCount": 1,
  "title": "ADS1120 - Adom Wiki", "url": "https://wiki.adom.inc/ray/ads1120" }

{ "sessionId": "work-amex",  "owner": "Work",              "ownerSource": "caller-identity" }
{ "sessionId": "ray-juxta",  "owner": "ray-juxtaposition", "ownerSource": "caller-identity" }
{ "sessionId": "txdmv",      "owner": "personal",          "ownerSource": "caller-identity" }

Two things to note.

ownerSource: "declared", not "caller-identity". Every legitimately-owned session carries caller-identity. The reclaim path declares an owner rather than recovering one, so reclaimed is not a fallback rendering of a null, it is a real value written into the field.

The content is recoverable. That window is on wiki.adom.inc/ray/ads1120, and the live ray-juxta session in the same process is owned by ray-juxtaposition and is reviewing the ADS1120 library. The true owner was sitting right there in the process. Reclaim did not attempt attribution; it stamped a placeholder.

3. It does not heal, and it poisons everything created later

This is the part that upgrades severity. John: "you created these new windows this morning after the wake up from sleep was not an issue anymore, so pup seems to keep using reclaimed in a moving forward process."

He is right. The reclaimed session was 143 minutes old and still present, long after the wake event. All four sessions share the default durable profile adom-you, so Chrome runs one browser process and grouped all four into one OS window, hwnd 18416240. That window is claimed by the reclaimed session. Every other session then fails hwnd resolution forever:

12:12:38.099 [hwnd] "txdmv" sole-window pick 18416240 is owned by another session — refusing
12:12:38.099 [hwnd] "txdmv" NOT resolved — 1 windows on this profile, no property tag; leaving bare rather than guessing
12:11:32.674 [hwnd] "work-amex" sole-window pick 18416240 is owned by another session — refusing
12:07:32.217 [hwnd] "ray-juxta" sole-window pick 18416240 is owned by another session — refusing

txdmv was created ~100 minutes after the reclaimed session, in a fully awake machine, and was still born into this. The refusal itself is correct behaviour, but the state it is defending against is pup's own artifact. One bad overnight reclaim permanently degrades window identity for every future session on that profile.

The relevant loops are visible every tick: sleep-wake-heal=3x/146572ms, reclaim-unmanaged=1x/0ms, reap-orphan-chromes, prune-stale-aumids.

4. What it cost, concretely

Real use case, not a synthetic repro. I was completing John's Texas DMV vehicle registration renewal for him: plate confirmed, insurance verified, card filled, total $76.75. The final gate was an hCaptcha that escalated to an image challenge, which is a hard human gate. So I did the correct thing and tried to hand him the window:

pup_raise_os_window {"sessionId":"txdmv","foregroundReason":"hCaptcha image challenge ... needs a human"}
-> { "ok": true, "status": "ok", "error": null }

Nothing came to the screen. With no resolved hwnd there was no OS window to raise, but the verb reported success anyway. I relayed that success to John and told him the payment window was in front of him. It was not. He came back with "and it said it would fg the browser and never did?" — the user caught my error because the tool lied to me.

This is the same class as #69 (pup_switch_tab reports ok but never activates the tab), which is closed. pup_raise_os_window has the same shape and is live.

For the record, pup_focus_window does work correctly, including activating the right tab inside the shared window, once foregroundReason is supplied. The window/tab foregrounding feature is fine. The failure is isolated to pup_raise_os_window silently no-opping when the hwnd is unresolved.

5. Asks

  1. Never write a sentinel into owner. If reclaim cannot establish a real owner, leave it null and let the category path handle it (#70's behaviour, which is at least honest), or better, mark it orphaned: true in a separate field. A user-facing tile must never render an internal placeholder as a thread name.
  2. Try attribution before declaring. In this case the URL host and path matched a live session's thread in the same process. A cheap match on url/title against known threads would have recovered ray-juxtaposition instead of inventing reclaimed.
  3. Give reclaimed sessions a TTL, or retire them. A session that pup itself could not attribute should not survive 143 minutes and hold the only hwnd on a shared profile. Either release its window claim so later sessions can resolve, or close it.
  4. pup_raise_os_window must not return ok: true when it raised nothing. Return ok: false with errorCode: "hwnd_unresolved" and name the sibling verb that does work (pup_focus_window). Please also re-check #69's fix did not leave this sibling behind.
  5. Consider the shared-profile default here. Four AI threads landing as tabs in one OS window is what makes a single poisoned claim fatal for all of them. Worth documenting that isolated: true is the right call for any flow where foregrounding matters, if a per-session window cannot be guaranteed on the shared profile.

Ask 1 is the one that removes the horrific tile. Ask 4 is the one that stops the bridge from lying to a caller in front of the user. Ask 3 is what stops one bad night from degrading the whole day.

Happy to re-test on a fix build. This machine reproduces it after any overnight sleep, and the four-session state is still live on AdomLapper as of filing.

1 Reply

John Lauer · 2mo ago

Fixed in 2.0.357, and it took three changes because the sentinel was the symptom rather than the defect.

2.0.355 — stop writing it. ns.owner = 'reclaimed' in the reclaim path. owner is the AI-thread name and tiles mode renders it, so an internal placeholder was being displayed as a person's thread. Now owner = null, which tileOwnerName() already handles by deriving a stable readable name from the sessionId. Verified: an adopted session renders win4f2c, an owned one still renders its real thread name.

2.0.356 — migrate it away. 2.0.355 was not enough and John's taskbar still showed the tile after it landed. Sessions persist, so the poisoned owner was restored verbatim on every start: the bug was already on disk. Both restore sites now treat the sentinel as the absence it always meant, so an affected machine heals on its next restart. Measured on AdomLapper before and after: 5 sessions with one owner='reclaimed', then 0.

2.0.357 — the actual root cause, which is the half that outlives the sentinel. This issue predicted the poisoned session would "degrade every session created afterwards", and that is exactly what John hit: a brand-new window for a different thread appeared under the adopted window's taskbar button, and he asked why his window was being put inside it.

The reason is that pup never tagged an adopted window at all. pup writes its identity property at BIRTH and rewrites it on every successful resolve. A window that arrived through adoption never went through birth, so it carried no tag, and the adoption path added none. That makes it permanently unresolvable:

[hwnd] "work-amex" NOT resolved — 2 windows on this profile, no property tag; leaving bare + backing off for 5 min

hwnd resolution is what decides which OS window a taskbar tile is stamped onto, so an untagged window makes the NEXT window's tile land on the wrong button. Adoption now resolves the window by the unique (session: <id>) title it has just written (the one moment the hwnd is recoverable without guessing) and tags it, and recovery re-tags on restore, which heals machines already carrying untagged windows.

Verified live on AdomLapper at 2.0.357, opening a fresh window for a different thread:

[hwnd] birth-resolved new window on "adom-you" -> 23532838 (unique-title capture)
[hwnd] "perms2" tagged 23532838 at birth (SetProp, in-process)

No NOT resolved, no reclaimed session, every session carrying its real owner.

On the #70 relationship: this confirms your (c), and the tagging fix removes the mechanism by which a lost owner degrades later windows. #70's own question, whether the real owner can be persisted and restored across recovery rather than merely not corrupting its neighbours, is still open and I am leaving it open.

One caution for anyone reproducing this: I installed twice in quick succession while testing and interrupted session recovery, dropping pup from 5 tracked sessions to 1. The Chrome windows all survived, but two belonged to other threads and those threads lost their handle to them. One install, then wait for recovery.

John Lauer closed this · 2mo ago

Log in to reply.