Completes the wait capability per review guidance ("add corresponding
execution hooks and fallback tests for the modes"):
- contract.WaitSpec restores event/auto modes with event_key,
match_field, and a new resource_query (dotted path into the accepted
result data yielding the identifier events correlate against);
per-mode validation of required fields
- internal/wait adds EventStream (leaf-owned transport) and RunEvent:
correlated-event filtering, the same terminal/pending/unknown mapping
as polling, timed-out pending on deadline during consumption, and an
ErrEventStreamEnded sentinel distinguishing stream termination from
fail-closed status errors
- Spec.WaitEvents hook; validateWaitDecl pairs mode with hooks
(poll<->WaitPoll, event<->WaitEvents, auto<->both; surplus hooks
rejected too)
- the wait phase runs event-first in auto mode and falls back to polling
when the stream ends or the subscription fails, under one deadline
spanning both phases; strict event mode surfaces stream errors
- output.CommandResult gains Data() (deep copy) so the framework can
resolve the resource identifier without exposing mutable state
Changed-code coverage re-verified at 100% (CI cross-package recipe).
Addresses the three P1 findings from review 4942891040:
1. event/auto modes were declared but always executed polls. WaitSpec now
accepts poll only (event/auto fail validation with a not-implemented
message); event_key/match_field dead fields removed. Event waiting will
land with its own execution path and mode constant.
2. a deadline reached during the between-poll sleep re-polled with a
cancelled context, so a context-aware poller surfaced its error as a poll
failure instead of the contracted timed-out pending. The wait between
polls now uses a timer + select on ctx.Done(), the deadline is checked
before each poll, and a poll error on a cancelled context closes as
timed-out pending with the last observed status.
3. legacy Invoke/Orchestrate/RunE commands declaring Wait observed a failure
terminal while still exiting 0. validateWaitDecl now requires the
ResultInvoke dispatcher (the only path whose unified envelope can be
closed); the wait phase no longer wraps legacy paths.
Also: dropped the unreachable nonPendingTerminal branch, simplified
waitTimeoutSecs to the flag value (registration always seeds the reviewed
default), and raised changed-code coverage to 100% (new wait-engine edge
tests, output With* unit tests, contractfinal deep-copy coverage,
AttachContract invalid-Wait panic path).