Repository navigation
Description
Summary
Add a dev-mode endpoint that lets an external tool drive a component's actions headlessly — create a session, render a page, enumerate its invokable actions, and invoke one by name — returning the resulting HTML. This closes the one remaining gap in the agent/tool dev loop: the exercise → observe half.
Motivation
The dev endpoints so far cover a lot of the loop well:
/ng/dev/eval— run code in the live JVM (great for inspecting state)./ng/dev/problems— read the runtime binding-error boxes as data./ng/dev/log, plus the Parslips editor-side/validateand/elementApi.
But a tool still cannot click. Component actions resolve by session-scoped, stateful element URLs (/wo/…/0.9.7.1.5), so curl can verify a direct route or a fresh render, but not a flow — a submit, a popup switch, an Ajax toggle. From a recent road test of an ng-objects app:
I can't click. This is the one big gap. Component actions live behind stateful, session-scoped element URLs, so curl can verify direct routes and fresh renders but not flows — I shipped the Ajax toggle, the popup switching, and the deep-search buttons verified only by template validation and reading the framework source. Every "give it one click in the browser" I asked of you today was this gap talking.
Since we own the request loop, we can play the browser without pulling in Selenium/WebDriver.
Proposed shape
A small state machine an agent can drive, mirroring the /ng/dev/* family (dev-mode-only; the parts that mutate app state should be loopback-gated like /eval):
- Start / render — create (or reuse) a session, render a named page (or a direct action), return the HTML plus a list of the invokable elements on it: each with its element ID, the action binding's keypath (e.g.
action = deleteItem), and enough to name it for a follow-up call. - Invoke — invoke one of those actions by name/ID within the session, run the action phase, and return the resulting page's HTML and its fresh action list — so the tool can chain clicks (open a page → click a tab → submit a form).
Rough surface (names TBD, aligned with the existing /ng/dev/* shapes and the sibling wonder-slim .woa/* mounts):
POST /ng/dev/render?page=SomePage -> { session, html, actions:[{id, name, keypath}] }
POST /ng/dev/invoke?session=…&action=… -> { html, actions:[…] } # run the action, return the next page
JSON envelope for the action list; the HTML as the body or a field. The session handle threads state across calls so a flow is a sequence of invokes.
Design notes / open questions
- Session + element-ID model. The action URL encodes a component tree path bound to a session's page cache. The endpoint needs to take the same context WO/ng builds during a normal request, enumerate the action-bearing elements (they're already discoverable during
appendToResponse/invokeActiontraversal), and expose stable handles. Decide whether to reuse real element IDs or assign friendlier per-render names. - Form values. A realistic "click submit" needs to set form field values first. The
invokecall should accept a map of form values to apply before running the action phase (take-values → invoke-action → append). - Shared engine, two mounts. Like
eval/problems: the driving logic belongs in a shared place (ng-core or an ng-appserver dev module) with thin route adapters in ng-objects (/ng/dev/…) and wonder-slim/ERExtensions (…/App.woa/…), so both runtimes behave identically. (wonder-slim's is the harder case — the WO request loop is less ours than ng's.) - Ties into Parser: record a SourceRange per binding (not just per element) #64. A per-binding source range (Parser: record a SourceRange per binding (not just per element) #64) would let each enumerated action carry a component/line location, which also satisfies the road test's other wish: runtime
/problemsentries that are directly actionable ("here's the component and line") instead of "now grep for it".
Scope
- In: render-a-page-and-list-actions, invoke-an-action-with-optional-form-values, session threading, the JSON envelope, dev-mode + loopback gating. ng first; wonder-slim parity to follow.
- Out (for now): JS execution / a real DOM, Ajax responses that a browser would apply client-side (return the server's response and let the caller reason about it), auth flows.
Context
Part of the app-side dev-endpoint family (/eval, /problems, /log) driven by the parslips-dev-loop Claude skill. The road test that produced this: /eval + /problems + /elementApi landed and verified against a real production code path; this is the explicitly-deferred "#2", the next step.
Activity