docs: teach agents to size an interactive island, not just place one - #1395
Merged
Conversation
The skill said "all interactivity lives in a component" in eleven places and never once said how much markup to put in it. Worse, the ownership rules pushed the wrong way with no stopping rule: "if you are writing a selector to find markup another file rendered, write the component that renders it instead" never terminates, because there is always more surrounding markup a component could render. The fact that would have stopped it was buried as the last bullet of the elidability blocker list, "being rendered by a component that itself ships", and components.md explicitly told the reader elision was "inspectable rather than something to reason about". So an agent had no reason not to wrap a page section in a component to make one button work, and nothing downstream would flag it. Adds the stopping rule (a component owns the markup its own behaviour reads or writes), the byte-cost argument behind it, a too-big/right-size before-and-after, a walk-the-template test, and the ownership-wins exception. Promotes the propagation fact from a blocker-list entry into a stated design consequence, since it is the reason the cost of an oversized island is not linear in what was moved. Also defines "island", which the skill used twice and never explained. Same guidance on the four surfaces that carry it: the skill (which create.js copies verbatim, so every scaffolded app gets it), the root AGENTS.md execution model, and /docs/components.
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.
Problem
The skill told an agent where interactivity goes in eleven separate places. It never told it how much to put there.
Worse, the ownership rules in
references/components.mdpushed actively the wrong way, with no stopping rule. Rule 1 says: "If you are writing a selector to find markup that another file rendered, write the component that renders it instead." Followed literally that argument never terminates, because there is always more surrounding markup a component could render. Nothing anywhere said where to stop.The one fact that would have stopped it was buried.
components.mdlisted "being rendered by a component that itself ships" as the last bullet of the elidability blocker list, a place an agent reads when debugging a verdict, never while authoring. And the file went on to say elision is "inspectable rather than something to reason about from the rules above", which tells the agent not to think about island cost at all while writing the component.Net effect: an agent had no reason not to promote a whole page section to a component so one button inside it could work, and no downstream check would flag it. All 19 bullets in the skill's "Common Mistakes To Avoid" pointed the other way, at under-componentizing.
The word "island" also appeared exactly twice in 3,440 lines and was never defined.
What this adds
The stopping rule. A component owns the markup its own behaviour reads or writes. Static markup that no handler touches, no state change re-renders, and no template hole depends on belongs to the page.
Why it is a byte rule and not a taste rule. A page never hydrates, so markup it renders is free in the browser. Markup an island renders is not: the module is fetched,
@webjsdev/corecomes with it, and on upgrade the component re-renders that subtree, replacing the server's DOM. Moving static markup across the boundary converts free HTML into shipped JavaScript that rebuilds what the server already sent.The non-linear cost, promoted out of the blocker list. Elision propagates downward from whatever ships, so island size decides how much of the tree stays elidable. Ten display-only components rendered by a page are ten modules the browser never fetches. The same ten rendered by one oversized wrapper all ship, and nothing about any of them changed.
A too-big / right-size before-and-after, a
<product-page>swallowing a whole article versus an<add-to-cart>that is one button.A test an agent can run on a component it is about to write. Walk the template and ask of each element: does a handler touch it, does a state change alter it, does it sit in a hole? Three noes means that element is a passenger. A template that is mostly passengers wants splitting.
The exception, stated so the rule does not get over-applied. Markup that is static today but is what a near-term behaviour will read is fine to keep, since the alternative is a component reaching outward for it later, which rule 1 forbids. When ownership and bytes genuinely collide, ownership wins.
A definition of "island", in the
SKILL.mdexecution-model block that already used the word.Surfaces
.agents/skills/webjs/SKILL.md.agents/skills/webjs/references/components.md.agents/skills/webjs/references/routing-and-pages.mdAGENTS.mdwebsite/app/docs/components/page.tsThe scaffold needed no separate edit:
packages/cli/lib/create.js:679copies the repo-root skill verbatim into every generated app.Verification
Generated a fresh app with the built CLI and confirmed all four additions are present in its
.agents/skills/webjs/.test/repo-health/+test/docs/149/149,test/scaffolds/65/65, websitenpm run typecheckclean,webjs checkclean, website tests 471/471.