fix(widgets): require fresh source view before mutating an existing widget#30
Open
nsyring wants to merge 1 commit intoagent0ai:mainfrom
Open
fix(widgets): require fresh source view before mutating an existing widget#30nsyring wants to merge 1 commit intoagent0ai:mainfrom
nsyring wants to merge 1 commit intoagent0ai:mainfrom
Conversation
The space-widgets skill and onscreen-agent system prompt had no rule forcing readWidget before patchWidget, renderWidget, or upsertWidget on an existing widget id. When the user asked for a small behavior change, the agent would routinely skip readWidget and rewrite the entire renderer from rendered HTML or from memory, producing 500+ lines of regenerated code in place of a 5-line patch. The architectural intent already lives in spaces/AGENTS.md but did not reach the prompt-facing skill. This change adds: - a "read before mutate" subsection in the space-widgets SKILL.md that defines what counts as a fresh source view and forbids reconstruction from memory or from seeWidget rendered HTML - a matching "behavior change requires fresh source" staged turn rule in the onscreen-agent system prompt so the rule fires per turn Complementary to PR agent0ai#6 (which covers the recovery path after a patchWidget error). This PR covers the case where the agent never attempts patchWidget at all.
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.
fix(widgets): require a fresh source view before mutating an existing widget
Summary
Adds a "read before mutate" rule to the space-widgets skill and a matching staged-turn rule to the onscreen-agent system prompt. The agent must hold a fresh
readWidget(id)/patchWidget(id)/renderWidget({id})/reloadWidget(id)success from the live conversation before calling any ofpatchWidget,renderWidget, orupsertWidgeton an existing widget id. If the only available source view is older than the most recent successful read-or-write on that id, or trimmed by the prompt-budget middle-replacement, the agent must callreadWidget(id)first and patch on the next turn.Why
app/L0/_all/mod/_core/spaces/AGENTS.md:196already states the architectural intent:That guidance lives in the developer-facing AGENTS.md but does not reach the prompt-facing skill or system prompt. The result is a recurring pathological pattern that I reproduced reliably during local widget development:
Current Widgetsource for that id (e.g. the prior conversation never read it, or the source has been mid-trimmed by the long-message placeholder mechanism inapp/L0/_all/mod/_core/agent_prompt/prompt-items.js)readWidget, infers structure from rendered HTML or from a guess, and rewrites the entire renderer viaupsertWidget/renderWidgetThis is not a
patchWidgetfailure-loop case (where the framework already explicitly says "use renderWidget"). The skill currently has rules covering that case but no rule covering "user asked for a behavior change without a fresh source view" — so the agent treats the implicit choice as open and frequently picks the destructive option.What changed
app/L0/_all/mod/_core/spaces/ext/skills/space-widgets/SKILL.md(+7)New
read before mutatesubsection placed directly after the existingpatch vs rewritesubsection. It defines what counts as a fresh source view, requiresreadWidget(id)first when none is available, forbids reconstruction from memory or fromseeWidget()rendered HTML, and explicitly states that a generic user complaint is not a full-rewrite signal on its own.app/L0/_all/mod/_core/onscreen_agent/prompts/system-prompt.md(+6)New
behavior change requires fresh sourcestaged turn rule, placed in the existing turn-rule list directly afterfresh read then do it. It mirrors the SKILL.md guidance at the prompt-staging granularity that the system prompt uses elsewhere (visible defect repair,framework-corrected rewrite continues, etc.) so the rule actually fires per-turn.No code changes, no new dependencies, no test updates needed. Mirrors the change footprint of #6 (also a SKILL.md-only contribution targeting widget-edit hygiene).
Relationship to other PRs
patchWidgeterror ("call readWidget before retrying"). This PR covers the path before any mutation attempt on an existing widget, including the common case where the agent never triespatchWidgetat all and goes straight torenderWidget/upsertWidget.Test plan
This is a prompt and skill text change. There are no automated tests for the skill body in
tests/(none of the existing skill files are covered by unit tests, and the existingtests/spaces_*.mjsexercise widget-import resolution and prompt-context, not skill content), so verification is qualitative.node --checkpasses on adjacent JS files (no JS touched, but verified working tree is otherwise clean)npm run desktop:packbuilds:readWidget(id)first and a targetedpatchWidgeton the next turn, instead of the previous immediaterenderWidget/upsertWidgetfull rewriteOut of scope (possible follow-ups)
prompt-items.jsto keep the source view byte-stable across turns, so the "fresh source view" guarantee survives long histories without re-reading. Tracked separately.Current Widgettransient section astrimAllowed: falseso the freshly-published source survives transient-budget trimming. Tracked separately.spaces/storage.jswhenrenderWidget/upsertWidgetoverwrites an existing widget id without a recentreadWidgeton that id. Could promote the skill rule into structural enforcement; left for a follow-up so this PR stays a pure skill-and-prompt change.🤖 Generated with Claude Code