Context
The bounded loop audit found eight matching Boolean wait bodies across Workstation and DemoB. The other seven matching loop headers contain gesture actuation, receipt returns or readiness snapshots and are excluded. This is one small implementation cut under the existing workspace-decomposition epic.
The Problem
Each consumer repeats attempt bounds, delay scheduling, early success and final predicate sampling. Those mechanics depend only on the instance lifecycle, while the predicates depend on the consumer. Copying the mechanics makes their destruction and retry behavior independently editable.
The Architectural Reality
src/core/Base.mjs already owns timeout() and trap(). Its timeout registers a rejection which destroy() settles with Neo.isDestroyed. Both consumer classes inherit Base through their ordinary families; no dock-specific collaborator or configuration bridge is needed.
A live import probe reports Base.prototype.waitFor === undefined, with timeout and trap both functions. Source searches across src, apps and examples found no conflicting waitFor method.
The Fix
Add Base#waitFor(predicate, {attempts=120, delay=16}={}) for a synchronous Boolean predicate. Reuse this.timeout(delay); retain the existing loop's immediate sample, bounded waits, early success, and final predicate read. Propagate predicate exceptions and timeout rejection.
Replace only the eight simple Boolean wait bodies in apps/workstation/view/Workspace.mjs and examples/dashboard/crossWindow/DemoBWorkspace.mjs. Keep their predicates, caller defaults and public methods. Extend the existing core timeout unit spec and regenerate the tracked size baseline.
Contract Ledger
| Surface |
Authority |
Behavior |
Fallback |
Docs |
Evidence |
| New Base.waitFor |
Existing Base timeout/trap lifecycle |
Synchronous predicate sampled immediately; at most attempts waits; true exits early; one final sample after exhaustion |
Boolean false on exhaustion; exceptions and destruction rejection propagate |
Method JSDoc |
Immediate/delayed success, exhaustion/final sample, throwing predicate, actual destroy |
| Eight consumer waits |
Existing consumer predicates and signatures |
Delegate repeated polling mechanics through inherited Base method |
Preserve current attempts/delay and return values |
Existing consumer intent retained |
Focused consumer suites and full unit suite |
Acceptance Criteria
Out of Scope
The seven gesture/snapshot/receipt loops; async predicates; event subscription machinery; native park/admission/recovery changes; other factory conversions; modifying existing approved PRs; Brain/tooling work.
Avoided Traps
A new helper class or controller with consumer callbacks/configs; putting consumer predicates into Base; treating all matching loop headers as equivalent; converting destruction rejection into false; changing retry semantics while extracting them.
Decision Record impact: aligned-with ADR 0029; no docking or native authority changes.
Decision Record: no new ADR required for this additive lifecycle utility.
Related
Parent: #18304
Live latest-open sweep: latest20 open issues checked at2026-09-06T22:45Z; no equivalent found. Latest30 all-status A2A checked in the same window; Vega owns remaining triage but explicitly has no implementation claim on this cut. Exact all-state waitFor search found the parent and unrelated old test tickets. MC problem/rationale searches returned off-topic results, no applicable prior decision. Own-assignment sweep: existing VesselPark, guide, film and launch obligations do not own this polling cut. Labels verified. Structure-map command exits1 without output; placement rests on the existing Base timeout/trap methods. Vega received the concrete Base placement and exclusions in MESSAGE:bb9b5401-1fd5-4703-800f-ba2d65e18a0a.
Origin Session ID: 10c286a7-65e7-4f15-be8e-8dace71af32d
Retrieval Hint: bounded Boolean predicate loops Base timeout destroy rejection Workstation DemoB
Context
The bounded loop audit found eight matching Boolean wait bodies across Workstation and DemoB. The other seven matching loop headers contain gesture actuation, receipt returns or readiness snapshots and are excluded. This is one small implementation cut under the existing workspace-decomposition epic.
The Problem
Each consumer repeats attempt bounds, delay scheduling, early success and final predicate sampling. Those mechanics depend only on the instance lifecycle, while the predicates depend on the consumer. Copying the mechanics makes their destruction and retry behavior independently editable.
The Architectural Reality
src/core/Base.mjsalready ownstimeout()andtrap(). Its timeout registers a rejection whichdestroy()settles withNeo.isDestroyed. Both consumer classes inherit Base through their ordinary families; no dock-specific collaborator or configuration bridge is needed.A live import probe reports
Base.prototype.waitFor === undefined, withtimeoutandtrapboth functions. Source searches acrosssrc,appsandexamplesfound no conflictingwaitFormethod.The Fix
Add
Base#waitFor(predicate, {attempts=120, delay=16}={})for a synchronous Boolean predicate. Reusethis.timeout(delay); retain the existing loop's immediate sample, bounded waits, early success, and final predicate read. Propagate predicate exceptions and timeout rejection.Replace only the eight simple Boolean wait bodies in
apps/workstation/view/Workspace.mjsandexamples/dashboard/crossWindow/DemoBWorkspace.mjs. Keep their predicates, caller defaults and public methods. Extend the existing core timeout unit spec and regenerate the tracked size baseline.Contract Ledger
Acceptance Criteria
Neo.isDestroyedrejection during a pending wait.Out of Scope
The seven gesture/snapshot/receipt loops; async predicates; event subscription machinery; native park/admission/recovery changes; other factory conversions; modifying existing approved PRs; Brain/tooling work.
Avoided Traps
A new helper class or controller with consumer callbacks/configs; putting consumer predicates into Base; treating all matching loop headers as equivalent; converting destruction rejection into false; changing retry semantics while extracting them.
Decision Record impact: aligned-with ADR 0029; no docking or native authority changes. Decision Record: no new ADR required for this additive lifecycle utility.
Related
Parent: #18304
Live latest-open sweep: latest20 open issues checked at2026-09-06T22:45Z; no equivalent found. Latest30 all-status A2A checked in the same window; Vega owns remaining triage but explicitly has no implementation claim on this cut. Exact all-state waitFor search found the parent and unrelated old test tickets. MC problem/rationale searches returned off-topic results, no applicable prior decision. Own-assignment sweep: existing VesselPark, guide, film and launch obligations do not own this polling cut. Labels verified. Structure-map command exits1 without output; placement rests on the existing Base timeout/trap methods. Vega received the concrete Base placement and exclusions in MESSAGE:bb9b5401-1fd5-4703-800f-ba2d65e18a0a.
Origin Session ID: 10c286a7-65e7-4f15-be8e-8dace71af32d Retrieval Hint: bounded Boolean predicate loops Base timeout destroy rejection Workstation DemoB