docs(exploration): explore lessons from BuilderIO/agent-native - #630
Merged
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
Preview removed for PR #630. |
crs48
added a commit
that referenced
this pull request
Jul 26, 2026
…owns any pixel (#631) Implements **W1–W3** of [exploration 0399](docs/explorations/0399_[-]_POINT_AND_CHANGE_XNET_EDITING_ITSELF.md). Marked `[-]` — 25/30 items; the four deferred ones are listed in the doc with reasons. > Stacked on [#630](#630) (explorations 0397–0399), so it targets that branch. Retarget to `main` once #630 merges. ## What you can do now Hold <kbd>⌥</kbd> and hover: the element under the pointer is outlined and labelled with the **layer that owns it** and one sentence on what changing it would move. <kbd>⌥</kbd>-click opens a panel that leads with that sentence, then offers the smallest control that can make the change. | Pointed at | Verdict | Control | | --- | --- | --- | | Something painted from a theme token | Lane 1 · global scope | the token's value, one Undo | | A registered panel | Lane 1 · surface scope | that slot's own palette commands | | A plugin surface | Lane 2 | explanation only (see deferred) | | `packages/*` / `apps/*` source | Lane 3 | explanation only (dev-loop is node-side) | | `packages/{sync,crypto,identity,data}` | **refused**, with a reason | none | Verified live in the running app, not just in tests: all five verdicts, plus apply-and-undo writing and clearing a real token. ## Three decisions worth reviewing **The stamp is a JSX runtime shim, not a Babel plugin.** A `JSXOpeningElement` visitor never fires here — `@vitejs/plugin-react` doesn't transform JSX under the automatic runtime; esbuild does, earlier. By the time Babel runs, every `<div>` is already a `jsxDEV(...)` call. So `jsxImportSource` points at a shim that reads the `source` argument esbuild already passes. Side benefit: it doesn't depend on React storing it, so it survives React 19 dropping `_debugSource`. **Lane ≠ blast radius.** A Lane 1 token edit is the cheapest mechanism available and repaints the whole app; a Lane 3 edit drags a PR through CI and may move one button. `Resolution` carries both, and the UI shows the *scope* sentence — otherwise "make this blue" quietly restyles everything. **No per-element override store.** `packages/ui/src/theme/tokens.ts` is the token contract as plain functions; `ThemeProvider` delegates to it. Keyed by custom-property name, never by element — a per-element map would be a second source of truth and permanently fork the app's appearance from its stylesheet (the documented Plasmic failure mode). ## Safety - **The stamp can never ship.** Three guard tiers in `scripts/guard-no-source-stamp.mjs`: the config must gate on `command === 'serve'`, no source file may import the shim by name, and no built asset may contain the attribute. Verified against a real production build (0 hits) and verified to go red when deliberately broken. Wired into CI. - **Kernel packages are refused twice** — in the browser resolver and again in `assertEditable()`, which re-derives from the source ref. A test forges an `allowed: true` kernel resolution; it is still refused. - **`previewWorktree()` refuses the session's own port.** Sharing it is how a broken edit takes down the surface you're editing from. - **PRs open as drafts** and nothing merges. - **Injection boundary:** `lane3Prompt()` takes a `Resolution` (source location) and the user's instruction. The pointed element's rendered text — workspace content, possibly authored by someone else — has no field to travel through. ## Four bugs found while building, all now regression-tested 1. A bare function passed to `setUndo` — React treats it as an updater and **called** it, un-applying the change immediately. 2. Token attribution compared HSL triples (`0 0% 98%`) against computed `rgb(...)`; they can never match, so Lane 1 silently did nothing. 3. Tailwind's preflight sets `border-color` on **every** element, so an unguarded border check made nearly the whole tree claim Lane 1 and hid Lanes 2 and 3. Attribution now requires a drawn border, and text colour requires text the element owns. 4. The overlay is mounted at the app root (to cover onboarding and loading screens), which put it outside `App`'s `ThemeProvider` — `useTheme()` threw and took the whole app down. That's the self-bricking hazard in miniature; a test now renders the panel with no provider. ## Checks `turbo run typecheck` 97/97 · `vitest run` 11,267 passed / 3 skipped · lint clean · guard green · changeset for `@xnetjs/devkit` + `@xnetjs/ui` (both minor) · changelog fragment added. Also points 0190 at this doc — its dev-loop body was built and had no front end. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds exploration 0397 — a depth-1 code read of BuilderIO/agent-native (
d296fd9, 26 Jul 2026) and what transfers to xNet.What the repo actually is
4,037 stars, created 12 Mar 2026, 2,417 PRs in ~4.5 months, 374,334 non-spec LOC in
packages/core/srcacross ~110 subsystem directories, 639 spec files, 17 templates, 65 skills, 43 guard scripts. Agent-written, and it reads like it.The one load-bearing idea:
defineActionhas seven callers —"tool" | "http" | "frontend" | "cli" | "mcp" | "a2a" | "automation"— and every dispatch site tags which one.The finding for xNet
xNet has four parallel verb vocabularies that never meet:
WorkspaceCommand(palette/hotkeys),AiToolEntry(32xnet_*tools), the React mutation hooks, and a differentdefineActionfor outbound webhooks. The data layer is shared; the verb layer is not.The seam to fix it already exists —
apps/web/src/plugins/workspace-agent-module.tsalready gives the assistant tools that work by emitting the same registered commands the palette runs, never private state.Recommendation
Option C — promote
WorkspaceCommandinto the agent surface as an opt-in projection (absentagentblock = palette-only, so adding a palette entry can never silently widen agent reach), sequenced behind two cheaper wins:// guard:allow-… — reasonopt-outs, including a false-checkbox guard (0377 recorded five falsely-checked[x]items).VerbCallertag routed through the existing audit recorder.ui://,text/html;profile=mcp-app); we already havexnet://and the frame primitive.Rejected: adopting the framework (server-first Drizzle + Nitro, structurally opposite to local-first) and porting
defineActionwholesale (name collides with our outbound one; four of the seven dispatchers have no consumer here).Notes
needsApproval?: boolean | predicate) is weaker than ours (risk tiers, chat codes, audit,xnet_undo). Keep ours; steal only its declarativeness — the gate belongs on the verb.packages/core/package.jsonsays MIT, rootpackage.jsonsays ISC, and there is noLICENSEfile — the GitHub API reportslicense: null.18 implementation items, 9 validation items. Docs-only — no changeset.
🤖 Generated with Claude Code