[Demo only DO NOT MERGE] feat(tutorial): silent-by-default copilot, capability-gated guidance, and the Code: Lita series - #3355
Open
Ethanlita wants to merge 98 commits into
Open
Conversation
…eries - Show tutorial-specific copy in the leave-editor confirm dialog when a course is active; keep the original copy otherwise - Make the course success modal's "back to course series" button navigate to the current course's series instead of /tutorials Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a one-shot skip flag on Tutorial so explicit, expected navigations away from the editor do not trigger the leave confirmation: - "Back to course series" in the success modal requests the skip before navigating to the series page - startCourse requests the skip before pushing the course entrypoint, which may point to a non-editor route Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A plain one-shot flag could leak: navigations that don't reach the editor's onBeforeRouteLeave (e.g. editor -> editor route updates when starting the next course, or starting a course from a non-editor page) never consume it, so a later genuine leave would silently skip its confirmation. Expire the skip request after skipLeaveConfirmTimeout instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Request the leave-confirm skip before emit('cancelled') so the time-bound
window isn't shortened by the cancel handler
- Wrap "back to course series" navigation in useMessageHandle to surface
router.push rejections, consistent with handleStartNextCourse
- Use sentence case for the button label: "Back to series courses"
- Align the tutorial.ts comment with the actual button label
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Invert the dependency so the editor no longer needs tutorial knowledge. Add an editor-owned `editorLeaveConfirm` controller exposing a generic message override and a one-shot, time-bound skip. Tutorial drives it the same way it drives copilot (TutorialRoot sets the tutorial-specific copy while a course is active; startCourse and the success modal request the skip), and the editor page's leave guard only reads the controller. The editor stays unaware of tutorials (no components/editor -> tutorials import), and pages/editor no longer imports useTutorial. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instead of auto-applying patches or simply point to the editor area using a purple arrow, Copilot now guides the user to make the
edit themselves in the editor, so children practice writing code by hand.
A reply can render four guide elements(custom elements), each backed by an in-editor overlay:
- [NEW] code-drag-hint — opens a drop target at a line and highlights the matching
API reference item, for inserting a new statement by drag
- [NEW] code-type-hint — opens a pre-indented blank line with a "Type the code here"
chip and a green reference ghost, for typing a new statement
- [NEW] code-delete-hint — highlights a range in red, for deleting it
- [MODIFIED] code-change-hint — shows the deletion (red) and addition (green) at once, for
replacing existing code (inline or whole-line)
The CodeGuideController keeps a single active guide, navigates to it, and
auto-dismisses it once the suggested edit is done (whitespace-insensitive).
Completion for delete/change is region-local: an invisible Monaco decoration
tracks the target range so it stays correct even when the user edits elsewhere.
Shared logic lives in use-code-guide.ts (re-show on stream settle, reopen on
click, skip-if-already-done, blank-line scaffolding) so all elements behave
consistently.
Changes to be committed:
new file: node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
modified: spx-gui/src/components/copilot/CopilotRound.vue
modified: spx-gui/src/components/copilot/context.ts
modified: spx-gui/src/components/copilot/copilot.ts
modified: spx-gui/src/components/editor/copilot/CodeChange.vue
new file: spx-gui/src/components/editor/copilot/CodeDeleteHint.vue
new file: spx-gui/src/components/editor/copilot/CodeDragHint.vue
new file: spx-gui/src/components/editor/copilot/CodeTypeHint.vue
modified: spx-gui/src/components/editor/copilot/index.ts
new file: spx-gui/src/components/editor/copilot/use-code-guide.ts
modified: spx-gui/src/components/xgo-code-editor/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/CodeEditorUI.vue
modified: spx-gui/src/components/xgo-code-editor/ui/api-reference/APIReferenceItem.vue
modified: spx-gui/src/components/xgo-code-editor/ui/api-reference/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/code-editor-ui.ts
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/CodeGuideUI.vue
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/index.test.ts
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/common.ts
…rials Let a tutorial course focus the "API References" panel on just the APIs it uses, so learners are not distracted by the full catalog. - Generic editor: add an `apiReferenceFilter` predicate on CodeEditor; the API reference controller derives the shown items from it synchronously, so the panel updates reactively without re-running the async provider. - Copilot: an `api-reference-filter` custom element narrows the panel to a set of API definition ids, and a `list_api_reference_items` tool lets the copilot look up the exact ids to use. - Tutorial: the element is registered only while a course is active and the filter is reset when the course ends; the course prompt instructs the copilot to narrow the panel to the APIs the whole course uses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only the success modal's course navigations leave the editor as an explicit, expected action; other startCourse callers (course-start / course-series pages) are not leaving an editor. Request the skip at the success modal call site instead of inside startCourse, so it no longer applies to every caller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ride Use a single leave-confirmation message for all effect-free cases instead of a tutorial-specific one. The copy also drops the "if you leave now" clause, which could imply the edits are saveable later. editorLeaveConfirm now only exposes the one-shot skip, and the tutorial layer no longer drives the confirmation copy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng, navbar course entry & story video During a tutorial course the editor switches to a focused workspace layout: the game preview takes ~48% of the workspace (min-width 660px), run/rerun/stop float at the bottom-right corner of the preview, and code is rendered with a larger fixed font. Which workspace areas get hidden (resource panels, edit-mode switch, preview header, code editor tools) is decided by the copilot via a new <workspace-hidden-areas> custom element, following the api-reference-filter pattern: the editor exposes a generic workspace-layout extension point and stays ignorant of who drives it. Also: - The navbar now hosts the in-course state entry with the exit-course action (via a new EditorNavbar slot filled by the editor page). - Exiting a course navigates back to the course list page; the course is kept if the navigation is aborted. - A story video modal (configured via VITE_TUTORIAL_STORY_VIDEO_URL) is shown on the course start page before the course begins; course items on the series page now link to the start page instead of starting directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restarting a course reloads the editor's project state (dropping in-memory edits of the effect-free editing session) and starts the copilot session over. The editor exposes this as a generic reload extension point (editor-reload.ts) driven by the tutorial, keeping the editor ignorant of who requests it. The navbar course entry gains a confirm-guarded "Restart course" menu item. The course start page now accepts a `video` query param to specify the story video per link. Since the param is attacker-controlled, only URLs on the current origin or the usercontent host are accepted; anything else falls back to the configured default. A per-course field on the Course API should replace this once the backend supports it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…able code in chat The course start page now runs an opening sequence: the story video, then an optional text guide the course author embeds in the course prompt as a <course-prelude> section — no backend change needed; the copilot is told the prelude was already shown. The course success element accepts a copilot-written `comment` evaluating the user's actual solution, shown in the success dialog together with a new "Try again" action that restarts the course. Completion guidance now treats goal criteria from the course prompt as the source of truth instead of exact code match. Topics gain a generic `hideCodeInChat` flag (set by tutorial courses): code blocks and code-hint elements keep driving their in-editor guides but no longer display copyable code in the chat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The course copilot gains richer, capability-first ways to guide beyond chat text: - Spotlight now supports a mask mode: everything except the revealed element is dimmed with a soft-edged overlay (never blocking pointer events), with the tip bubble sitting in the dimmed area. The new <spotlight-hint> element triggers it proactively on message arrival (gated to the live round). - <guide-modal> shows must-not-miss guidance in a centered modal, reopenable from a chip in the chat. - <api-video> plays the globally reused explainer video of an API from a library keyed by API definition ID; watched APIs are tracked as learned. - <stay-silent> lets the copilot explicitly do nothing: silent rounds are skipped by the chat UI (falling back to the last meaningful round) and are shown only in developer mode for prompt debugging. The course topic now instructs the copilot to stay silent by default on user events, intervening only when the user is stuck, asks, or needs a genuinely undiscoverable instruction — and to prefer in-editor guides, spotlight, videos and modals over text when it does respond. Also surface sign-in failures (e.g. unregistered redirect URI) as an error toast instead of silently doing nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The context message (custom-element definitions + ambient context + topic instructions) is a single API message, and the backend rejects messages longer than `copilotMessageContentMaxLength` — a tutorial course session (long course prompt, many registered elements, full editor UI info and preloaded skill documents) exceeded it, failing every round with "invalid content: text too long". Truncate the ambient context to the remaining budget, keeping the custom-element definitions and topic instructions intact: they drive the copilot's behavior, while the ambient tail (skill documents etc.) is re-loadable through tools. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e & add step explainer video entry The guide modal is for the one thing the user must not miss, so its content is now raw plain text (no markdown, no nested elements), without a title, and the copilot is instructed to keep it within 30 characters. The API video library gains its first entry (Sprite.step), served from public/tutorial-api-videos/ during development. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…at course start Interventions now follow an escalation ladder: a stuck user first gets one short plain-text hint via the guide modal (direction only, never the answer), then a spotlight / API video, and concrete code guides only after those failed or the user asks for the solution. The guide modal is repositioned accordingly: an intervention tool for stuck users, not a course-opening banner. Course authors declare the new knowledge points of a course in its prompt; the copilot shows the matching explainer videos at the course start, and shows none when no knowledge points are declared. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The course start is now explicitly silent setup: the reply to "Course Started" carries only the invisible setup elements plus declared knowledge-point videos — no greeting, no goal restatement, no proactive pointers to UI locations. The speak-up conditions drop the "next step needs an instruction" loophole: interventions happen only on direct questions, repeated failures (one failure is not enough), or sustained deviation. The example dialogue is rewritten accordingly — the old one demonstrated a chatty welcome with step-by-step highlight links, which taught exactly the proactive behavior the protocol forbids; the new one demonstrates silent setup, staying silent through exploration and first failure, a directional hint on repeated failure, a spotlight only when asked, and success with an evaluation comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The opening reply occasionally carried a sentence narrating the setup
("Let me set up the workspace..."); the protocol now states explicitly
that the reply is the setup elements and nothing else.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uto-open the panel Every user event used to pop the copilot panel open, so collapsing it during a tutorial course (where events are frequent) was futile. Closing the panel by the fold button or by dragging it out of bounds now counts as an explicit collapse: events keep feeding the session so the copilot keeps perceiving, but the panel stays closed until the user opens it again or a new session starts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed API videos
The copilot gains a UI-mode extension point ('floating' | 'docked'); the
tutorial docks it during a course. In docked mode a fixed circular trigger
sits at the bottom-right corner with the panel anchored above it (arrow
tail, resizable height via the dragger, persisted); clicking the trigger
or anywhere outside dismisses the panel as an explicit collapse. The
focused-layout run controls move beside the trigger, matching the
game-window design.
API explainer videos no longer render a card in the chat: the element
opens the video modal directly when its (live) message arrives, and the
copilot re-emits it on request. The course protocol now also forbids
re-emitting setup elements on later events — re-emitted videos would pop
over whatever the user is doing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… is on screen During a course the copilot no longer only waits for user input: while it has no visible presence — no guidance modal / API video / in-editor code guide (tracked via a new visible-artifact registry on Copilot) and no visible reply in the open panel — an "Auto perception" event is sent every 5 seconds so it can observe the latest editor state. Perception pauses while the page is hidden, while a round is pending, and after 4 consecutive silent perceptions (the protocol asks for a hint on the 3rd; the cap guards against disobedience); any other user event resets it. The course protocol distinguishes the sources: auto perceptions are not user requests and follow the strict silence rules, while the "Next step" quick input is an explicit ask deserving an immediate (still restrained) response. Auto perception events do not auto-open the panel (notifyUserEvent gains an autoOpen option). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…thing on screen The "visible reply in the open panel" check treated any non-empty round content as visible, but a reply consisting of render-null elements only (the course setup: workspace hiding, API narrowing, api-video...) shows nothing — with the panel open it blocked auto perception forever right after the course start. Strip all known invisible element tags when judging whether a round produced user-visible output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…k API reference items The copilot occasionally re-emits <api-video> despite instructions (e.g. on auto-perception rounds), popping the video over the user repeatedly. Auto-play now happens at most once per API per session; re-emissions render a small chip the user clicks to (re)play, and the element description tells the copilot so. In the focused layout the API reference items render as draggable blocks — card surface with a grip-dots handle and grab cursor — so dragging them into the code editor reads as the obvious interaction. Wired as a pass-through prop (editor → CodeEditorUI → APIReferenceUI), keeping xgo-code-editor ignorant of the workspace layout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The silence protocol over-generalized: a plain greeting typed by the user is not a question, so the copilot classified it as an event needing no reaction and stayed silent — which reads as "the copilot ignores me", and breaks courses whose goal is interacting with the copilot itself. The protocol now distinguishes the two by their wire format: silence applies only to <event>-wrapped messages; anything the user typed always gets a response. Course-specific completion criteria (possibly non-coding, e.g. "send the copilot a message") explicitly take precedence over every generic rule, including silence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… leaked reasoning around it
The model occasionally narrates its inner reasoning before the stay-silent
element ("Let me check the current state... <stay-silent />"); the silence
filter required the element to be the entire content, so the leaked English
monologue was shown to the user. The protocol requires stay-silent to stand
alone — when it appears at all, whatever surrounds it was never meant for
the user, so its mere presence now counts as silence (in both the chat
filter and auto-perception counting).
The course protocol additionally forbids writing reasoning into any reply
and requires user-facing text in the user's language.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story-video URLs are origin-restricted (the `?video=` param is spoofable), so far to same-origin plus the usercontent CDN. Externally hosted intro videos (e.g. the series opener on S3) were silently dropped. Trust the tutorial video host too — sourced from the single `tutorialVideoAssetBaseUrl` constant that the API video library already uses — so an author can point `<course-story-video>` at it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The block style used to hide the value-edit helper (pencil chip) for every input, while the hover "Modify" button still showed for all. It now keys off the input type: the host passes the types to suppress, and both entry points — the pencil and the "Modify" action — consult it via the input helper controller. The tutorial hides the helper for plain literals and direction (integer / decimal / string / boolean / spx-direction), so those are typed/measured by hand, and keeps it for the pickers (color, key, effect, resource, ...). Normal mode passes no types and is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the what's-new overview to the current design: the course config now declares (and the frontend applies at course start) the API narrowing, knowledge-point videos, and a configurable code-judged completion signal; the copilot presentation is a shared chat body with a global floating shell and an editor-owned docked shell; and add the focused-editor redesign, the type-gated input helper, and the video hosting/CORS notes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A course now declares its in-editor opening as an ordered `opening` array in the jsonc config — prelude / knowledge-point video / spotlight steps, played strictly in the authored order by the frontend once the editor is up. Spotlight steps highlight the one control to act on next: an API-references item addressed by API name (`data-def-id` on the item, matched by the shared course API matcher) or a UI landmark addressed by its Radar name (new `Radar.getNodeByName`); the sequence advances when the user dismisses the highlight (new `concealed` event on `Spotlight`), and unresolvable targets retry then skip so they cannot stall it. The prelude therefore moves into the editor phase: courses with an `opening` inline their guide text as an opening step, retiring the pre-editor `<course-prelude>` modal for them. `<course-story-video>` stays a pre-editor tag (it plays before the editor exists and carries its own origin check). Courses without `opening` keep the legacy behavior (`videos` + pre-editor prelude) unchanged. Docs: authoring guides describe the `opening` schema and landmark names; what's-new updated accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The completing log line is trailed within moments by more ambient events (game output, the game exiting); each new event aborts the in-flight event round and answers with the full history, so the round carrying the "Course completed" evaluation request was routinely cancelled and its reply — the dialog comment — never arrived, leaving the success dialog on the 8s "Well done!" fallback. Accept the evaluation from whichever event round ends up carrying it: the comment watcher takes the first event round that completes with prose at-or-after the completion event, instead of requiring the round to be the one literally named "Course completed". The topic tells the copilot the same story — when a recent "Course completed" event has not been answered yet, the current reply must carry the sentence, whatever event triggered the round. Also make the comment's destination unmistakable: the evaluation-request event and the success element's description now state that the dialog's comment area is the only place the user sees the text (prose in hidden event rounds reaches nowhere), and a blank comment attribute counts as no comment so the async request still fires. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every new event aborts the in-flight event round, and a course completion is immediately trailed by ambient noise — the game's output and exit, and the "Modal opened" fired by the success dialog itself — so the round asked to write the dialog's comment was cancelled two or three times over, costing aborted LLM requests and routinely missing the dialog's comment window. Events now carry an `ambient` flag (editor perception in user-events, page/modal/notification signals in CopilotRoot): while a copilot-produced artifact is on screen (`hasVisibleArtifacts`), ambient events are dropped — the artifact is what the user is looking at, and nothing may supersede the round serving it. The success dialog registers as such an artifact, alongside the existing opening modals. Deliberate user actions (typed messages, quick inputs) and lifecycle events are unaffected. Verified live: completion → dialog instantly, evaluation comment arrives in ~3s with zero cancelled requests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The block-style API panel was a fixed share of the code column, clipping wide signature cards into a horizontal scroll. It now fits the widest card (max-content, clamped to [200px, 45%]) and is drag-resizable at its right edge: dragging takes over with an explicit width (persisted, its own storage key), double-clicking the handle restores the auto fit. The resize wiring is shared with the normal editor's sidebar handle. When a filter narrows the list, the per-category headers and dashed separators now hide together with the category sidebar: a handful of hand-picked items reads better as one plain list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
While the game runs the stage is the engine's canvas — live sprite positions are inside the wasm instance, so the ruler cannot measure. Instead of vanishing with the edit stage, the button now stays put as an unusable variant (grey glyph under a red slash, hover-only tip), so the tool doesn't appear to come and go. The toggle is extracted into RulerToggle.vue, shared by the edit stage and the running overlay. The running game is now also constrained to the largest viewport-aspect rect inscribed in the focused container — the same min-scale letterbox the edit stage applies — instead of stretching to the (non-4:3) container, which shifted the world subtly on every run. Verified: the runner iframe and the edit stage content measure identical rects. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The direction type leaves the block-style hidden set: it has a dedicated picker (the direction dial), which earns it the pencil and the hover "Modify" like the other pickers — only the plain literals (integer / decimal / string / boolean) stay keyboard-only. The prelude modal button reads just "开始 / Go". What's-new docs refreshed for the current state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The library had no `turn` entry, so the course that teaches it fell back
to the shared demo video; it now points at the produced turn.mp4. Since a
video explains the API rather than one overload, lookup also falls back to
matching by API name — another overload's ID, or a bare name, resolves to
the same video instead of dropping to the demo.
The ruler is not an API and has no entry in the API reference, so the
copilot could never discover a video for it. Explainer videos may now
cover non-API topics: the ruler is registered under a pseudo-ID and
`<api-video>` enumerates such topics in its description, which is the only
place the copilot can learn the ID from. It plays when a user asks how the
ruler works; no course opening declares it (a course can add it later with
`{ "video": "ruler" }`).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both video dialogs hard-coded a 16:9 box, but tutorial videos are not one shape: the explainers produced now are 4:3 (turn, ruler) while the older one is 16:9 (step — which is also the demo fallback for every API without its own video), and story videos vary per series. Either constant is wrong for some of the library, letterboxing the rest. The box now reads the intrinsic ratio from the video's loaded metadata, so each video gets its own shape with nothing to declare or keep in sync; the fallback shape applies only until that metadata arrives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8 tasks
A course series round-trips through a `.xbcs.zip` archive, and the importer takes it almost entirely at face value — one format check, then everything is trusted. A malformed archive therefore fails mid-import having already overwritten projects, or imports "successfully" into something broken. The sharpest edge: `xbpHelpers.load` turns every zip entry into a project file, so rebuilding a `.xbp` with `zip -r`, Finder, or any GUI archiver injects directory entries that each become a zero-byte project file with an empty name. A 31-course package picks up ~330 of them. The skill supplies a script (unpack / inspect / validate / pack) that writes file entries only and encodes what the importer actually requires — declared paths, entrypoint coverage, project type, length limits, asset referential integrity — plus the failures no static check can see: the ownership and partial-import states behind "the file is fine but it still won't upload", and edits that pass validation while leaving a course unfinishable. Linked into `.codex/skills/` and `.github/skills/` so Codex and Copilot read the same canonical copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ction The opening queue is built on /start (the course must be set before navigation so the workspace layout is in place when the editor mounts), but its steps were allowed to render there too: the first prelude flashed over /start, hid during the redirect, and popped up a second time once the editor loaded. The current step now exists only on /editor/ routes. Also pass the starting phase's progress as 1, not 100 — the loading bar renders fractions ×100, so the redirect briefly read "10000%". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…code The name is how code addresses the sprite (`stepTo Radish2`), and reading it off the stage then retyping it is exactly the step beginners fumble — a typo'd name is a confusing error. The label under the selected sprite is now a button: clicking it drops the name at the code cursor (inline insertion, so a cursor mid-word moves to the word end and gets a space), as one undoable action. Tooltip + radar landmark included, so a course can spotlight it. `insertInlineText` already existed on the UI controller; it is now part of its public interface alongside `insertBlockText`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Design: the browser's default controls look out of place and pop up on every mouse move. The explainer modal now plays like the API-reference hover card — autoplaying, muted, looping, no controls. Looping replaces seeking for these short silent demos, and closing the modal is the only control needed; the opening sequence already advances on close, so nothing listened for `ended`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the opening's editor-route gating, the hover-card-style explainer player, the clickable sprite-name label, the xbcs-package skill, and the course-start progress fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Rebuilds the tutorial experience around a different premise: a course is a playground the copilot
watches over, not a script it reads out. Based on the prototype in #3330.
Two new documents describe this in full — what this branch adds
(the change surface and the reasoning behind each mechanism) and
the Code: Lita series (what the 28 courses teach).
The core idea
The copilot is silent by default. It perceives everything through real editor events but says
nothing until the user is stuck, has drifted, or asks. How much it may do is not a matter of prompt
etiquette: an intervention level decides which guidance tools are even registered, so a tool
it must not use at this level does not exist for it.
api-videoonly+ guide-modal,spotlight-hint+ in-editor code guidesThe level moves on the model's own per-round verdict (
<user-progress-ahead/>/neutral/back),counted by the system. A typed question temporarily raises it — someone who asks deserves an answer.
Prompt rules alone proved unreliable with the backend model. The order that works is mechanism
first (unregister the tool), per-round reminder second, protocol prose last.
What's new
Waking on runtime output (not just errors) is what makes coding courses completable at all: the
win condition is a log line.
```jsoncblock in the prompt declares hidden panels andwhether the copilot starts open. Author intent, applied once, not a runtime copilot decision.
sprite also reads the signed turn angle, which is exactly the number
turnexpects. Threecourses are built on it.
<stay-silent/>hides the whole round; only typed rounds appear inchat.
/devtools/course-runner, dev-only) — drives the real WASM runtimeprogrammatically, so a course can be verified without clicking through the editor.
Editor extension points
Features drive the editor through owned extension points; the editor never imports tutorial code:
workspace-layout(focused mode, hideable areas, optional tools),leave-confirm,editor-reload,API-reference filtering, and the code-guides gate.
Testing
193 unit tests pass, 13 test files added — intervention levels and verdict counting, guidance
registration, course config parsing, content visibility, ruler math, workspace layout reset, code
guides, and the course-start flow.
Beyond that, all 28 courses of the Code: Lita series had their reference answers executed against the
real runtime via the harness, checking that every radish in each level is collected: 27 of 27
courses with code pass (course 1 has no code). That exercise surfaced three engine constraints now
documented and designed around — the Main-execution timeout on bare top-level code, inline array
literals failing to compile, and
onTouchStartonly firing on the transition into contact.Not included
The ~25MB of demo/story video assets under
spx-gui/public/tutorial-*/are intentionally left out ofgit; the
stepexplainer already points at a hosted URL and the rest should follow. Course contentitself lives in the backend, imported from a course-series file.
🤖 Generated with Claude Code