Skip to content

feat(runtime): L1 resumable executor (execActions) + readable tail + fragment index - #36

Open
joelgwebber wants to merge 7 commits into
mainfrom
feat/l1-executor
Open

feat(runtime): L1 resumable executor (execActions) + readable tail + fragment index#36
joelgwebber wants to merge 7 commits into
mainfrom
feat/l1-executor

Conversation

@joelgwebber

Copy link
Copy Markdown
Collaborator

The runtime primitive we drove JetBlue's checkout through all along, graduated
from spike. Additive; touches only the SightkickGlobal host/console API.

execActions(actions, args, options) -> ActionStatus

Runs an arbitrary Step[] with the same atomic step machinery as runTool,
but swaps the outcome envelope: instead of throwing / hanging at the first action
that doesn't cleanly resolve, it returns a structured status —
{ completedThrough, total, done, interrupt?{at, action, reason, observed}, remaining, remainingView }.

  • remaining is the untried tail beginning with the interrupted action — a
    transparent continuation the caller resubmits to resume, either blind (opaque
    continuation) or after editing a step (escape hatch).
  • remainingView is a readable {op, target} projection of that tail — keeps the
    predicate discriminators ([label*="…"]) instead of dumping raw locator JSON.

fragments() / projectFragments(ir)

Projects the loaded IR's tool steps into a flat, pluckable index —
{ id, tool, index, op, label, uses } — so an agent composing an execActions
list finds a step by the params it fills (uses includes firstName) instead of
scanning ir.tools[].steps and string-matching interpolation values.

Scope / non-scope

Both are additive methods on window.__sightkick (the host/console API we
drive via browser eval and the sightkick-debug flow) — not WebMCP tools:
boot.ts still registers only ir.tools on document.modelContext. This is the
primitive a future "fallback" exec tool would wrap; nothing here registers or
forecloses that.

Validated end-to-end driving JetBlue cart → checkout → contact; runtime suite
green (execActions interrupt/resume, readable-projection, and fragment-index
tests).

joelgwebber and others added 7 commits September 11, 2026 09:12
…fragment index

execActions(actions, args, options) runs an arbitrary Step[] with the same atomic step machinery as runTool, but swaps the outcome envelope: instead of throwing/hanging at the first action that doesn't cleanly resolve, it returns a structured ActionStatus (completedThrough, done, interrupt{at,action,reason,observed}, remaining). remaining is the untried tail beginning with the interrupted action -- a transparent continuation the caller resubmits to resume, blind or after editing a step. remainingView is a readable {op,target} projection of that tail (keeps predicate discriminators, not raw locator JSON).

fragments()/projectFragments(ir) expose the loaded IR's tool steps as a flat, pluckable index ({id, tool, index, op, label, uses}), so an agent composing an execActions list finds a step by the params it uses instead of scanning ir.tools[].steps.

Both are additive methods on the SightkickGlobal (window.__sightkick) host/console API -- NOT WebMCP tools (registration still exposes only ir.tools). This is the runtime primitive a 'fallback' exec tool would wrap later; nothing here registers or forecloses that. Validated end-to-end driving jetblue's checkout via browser eval; runtime suite green.

Signed-off-by: Joel Webber <joel@fullstory.com>
…it hook

Add a self-installing pre-commit hook (.githooks/, wired through the
package.json prepare script -> core.hooksPath) that rebuilds and re-embeds
the runtime bundle whenever a commit stages packages/runtime/src/**, plus
pnpm regen / regen:runtime / regen:skills / regen:inspector convenience
scripts. This keeps generator/runtimebundle/sightkick-runtime.js from
drifting, which CI's 'Embedded runtime bundle in sync' check otherwise
catches only after the fact.

Signed-off-by: Joel Webber <joel@fullstory.com>
…en-safe clicks) (#35)

* fix(runtime): reach inner-child handlers via subtree descent (offscreen-safe clicks)

clickElement routed its dispatch through document.elementFromPoint(centre) to land on the topmost node -- needed because a custom option often carries its handler on an inner child (jetblue: <jb-select-option> > div.body; a click on the outer node bubbles past the handler and no-ops). But elementFromPoint returns null for a point outside the viewport, so a far-scrolled/offscreen option fell back to the outer node and silently didn't commit.

Replace it with deepestElementAt(root,x,y): descend the element's OWN subtree to the deepest node whose getBoundingClientRect contains the centre, then dispatch there so the event bubbles up to whichever inner node holds the handler. Own-rects are valid off-screen, so it reaches the inner child AND survives far-scroll -- no viewport dependency, no settle loop. The elementFromPoint variant is preserved as clickElementAtPoint (documented, unused) for the narrow hit-whatever-paints-on-top case. Validated live on jetblue's month/day/year/state/country selects (atomic, no scaffolding); 54 runtime tests green.

Signed-off-by: Joel Webber <joel@fullstory.com>

* chore(runtime): resync embedded runtime bundle

The subtree-descent change to packages/runtime/src/dom.ts rebuilt the
runtime bundle, but the committed copy embedded in the Go binary was not
regenerated, so CI's 'Embedded runtime bundle in sync' check failed. Rebuild
+ re-embed via 'go generate ./runtimebundle/...'.

Signed-off-by: Joel Webber <joel@fullstory.com>

---------

Signed-off-by: Joel Webber <joel@fullstory.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
)

* feat(generator): desugar signal completion predicates into IR waits

Add a wait_for {signal: name} form and a step-level then: post-condition. Both reference a corpus signal (the SEP-0007 state-signal subset: a named boolean over a component's presence or a view's route) and desugar into an ordinary waitFor step -- a component ref becomes a present-wait on the component's selectors, a view ref a route-wait. Resolution runs against the whole corpus (Corpus.ResolveSignal), so a post-condition may name a subject in the destination view, outside the tool's ensure_view scope (the common click-continue -> wait-for-next-view case).

then: expands to the action step followed by the signal wait, inheriting the step's when so an optional step and its post-condition skip together. A step then completes only once a named signal holds, not merely when the action dispatched -- the fix for a click that reports done while accomplishing nothing.

Runtime is unchanged: the desugared steps are the query/route waits it already runs. Pins the sightmap library to the state-signals branch build for Corpus.Signals/ResolveSignal; bump to the released version once that lands.

Signed-off-by: Joel Webber <joel@fullstory.com>

* chore: add changeset for signal completion predicates

Signed-off-by: Joel Webber <joel@fullstory.com>

---------

Signed-off-by: Joel Webber <joel@fullstory.com>
PR #37 landed the signal-completion-predicates feature but kept generator/go.mod
pinned to the state-signals branch build (v0.31.3-0.20260910214719-f6ac005ba83a).
Now that sightmap v0.32.0 is released with Corpus.Signals / Corpus.ResolveSignal,
replace the pseudo-version with the release tag. go build + go test ./... green.

Signed-off-by: Joel Webber <joel@fullstory.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant