Guided lessons that run inside the console - #162
Merged
Merged
Conversation
Learn is back, rebuilt around what changed since it was parked: the UI is Cloudscape now, and Cloudscape ships the AWS console's own onboarding components. So the tutorial runs inside StackPort instead of asking the learner to read a page of cards and go elsewhere. A panel docks in the right rail, hotspots point at the actual controls, and verification calls the real API, so a step turns green only when the resource genuinely exists. Two ways through every step, same verification either way: do it here, with the control highlighted and the exact values to copy, or with the CLI, with the command already filled in and each flag explained. Verified steps also poll, so running the command in a terminal turns the step green on its own and moves the lesson forward without a click. The learner names their own resources. A lesson declares its variables, the server generates and persists them, and every instruction, command and field on screen is substituted, so nothing has to be edited before it can be run. The endpoint URL is substituted per request too. Nothing is a gate. Any step can be skipped, and a skip is recorded as a skip rather than as done, so it can be checked later. Verification is a record of what the learner built, not a toll. Four Cloudscape behaviours dictate the architecture and are documented in CLAUDE.md, because each of them breaks a lesson silently: the provider lives above the router, the tutorial object is built once per run, the object handed to AnnotationContext is never marked completed, and Next is gated on the next step's hotspot being mounted, which is what makes it wait for verification. Ships one lesson, Store a file, behind STACKPORT_LEARN. Closes #155, closes #156, closes #157, closes #158, closes #159.
The substitution test asserted the machine's own AWS_ENDPOINT_URL, which CI does not set. The endpoint is now fixed per test, so the assertion checks the substitution rather than the environment.
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.
Closes #155, closes #156, closes #157, closes #158. Closes the tracking issue #159.
Learn is back. Not the parked version: the verification engine was always the good part of it, but the presentation was a static list of cards and every step sent the learner out to a terminal. Two things changed since it was parked. The UI is Cloudscape now, so practising in StackPort transfers directly to the real console. And Cloudscape ships the AWS console's own onboarding components (
AnnotationContext,Hotspot, the tutorial data model), already installed.So the tutorial runs inside the console. A panel docks in the right rail, hotspots pulse on the actual buttons, and StackPort calls the real API to check the result, so a step turns green only when the resource genuinely exists.
What a lesson feels like
Open Learn (sidebar, or
g l), hit Start lesson, and you land on/resources/s3with the popover already pointing at Create bucket.Every step offers two paths and verifies the same way either one is taken:
Verified steps poll while the panel is open, so you can run the command in your own terminal and watch the step go green on its own, then watch the popover move to the next control. No clicking through. That moment is where the design budget went.
You name your own resources. A lesson declares its variables; the server generates them once, persists them, and substitutes them into every instruction, command and field. So the commands on screen can be copied and run without editing, and they still match tomorrow. Change the name under "Names used in this lesson" and everything follows, validated against the lesson's own rule.
Nothing is a gate. Any step can be skipped, and a skip is recorded as a skip rather than as done, so it can be re-checked later. Verification is a record of what you built, never a toll.
Delight, no cheese. No points, no streaks, no confetti. The completion screen tells you what now exists in your emulator, by name, and offers to take you there.
Lesson 1: Store a file
Five steps, about six minutes: find S3, create a bucket, look inside it, put a file in it, read back what S3 recorded. It teaches the things that actually bite people later: that bucket names live in a shared namespace, that a bucket is flat and folders are a fiction the console invents, that content type is a guess made at upload time and never revisited, and that one flag is all that separates a local command from one that hits a real account.
The security defaults are taught as knowledge rather than faked as controls: the copy says real S3 encrypts with SSE-S3 unconditionally and blocks public access by default, because that is true and worth knowing, and because a toggle that silently does nothing teaches a lie.
Backend
Revived by hand from the parked branch rather than cherry-picked, since that commit predates the Cloudscape work.
backend/learn/verify.pykeeps theCHECKSregistry and its two best properties: fresh boto3 calls that bypass the TTL cache, andservice_unreachablekept distinct from "not done yet". Addeds3_object_exists(which can say what is missing, unlike a count),s3_prefix_exists, andall_of.backend/learn/content.pyvalidates trail content at import. An unknownverify.type, a hotspot the UI never renders, a step with no way forward, an undeclared{{variable}}, a duplicate id, two steps sharing an anchor: all fail in CI rather than in front of someone trying to learn.backend/routes/learn.pyserves trails with substitution applied, andPOST /progresstakesforcefor a skip./api/learnis allowlisted inReadOnlyMiddleware, since progress is local state rather than an AWS write.STACKPORT_LEARN, surfaced to the UI aslearn_enabledinGET /api/health.Four Cloudscape traps
These came out of reading the compiled library, and each one breaks a lesson silently. They are in CLAUDE.md so the next person does not rediscover them:
AnnotationContextresets to step 0 whenevercurrentTutorialchanges identity. So the tutorial object is built once per run and step content is a stable element that reads live state from context. Rebuilding it on every verification would throw the learner back to step one.completed: truemakes every hotspot vanish. The object given toAnnotationContextalways hascompleted: false; the finished state is rendered by the panel.CloudscapeShellremounts on every navigation, because each page renders its own. So the provider lives above the router inmain.tsx. There is a test that fails loudly if anyone moves it.Trap 1 also turned out to be the mechanism for moving between steps: to go to step N, hand Cloudscape a tutorial that begins at step N, and its reset lands exactly there.
run.offsettranslates indices back, and the popover's step counter reads from a ref so it reports the position in the lesson rather than in the sliced object.Three things only the real app found
Worth writing down, because none of them would have shown up in tests:
AnnotationContextretains theonStepChangehandler from mount, sopathnameread from the render closure was whatever route the lesson started on. Every step change then "navigated" to the step's route and dropped the query string, kicking the learner out of the bucket they had just opened. It reads a ref now.auto-fallbackis a navigation problem, not a step change. When a step's anchor is not on screen, Cloudscape moves its popover to whichever anchor is. Following that in the panel walked the learner backwards. The panel now holds its position and tells them where to go, and the next completion resyncs both.Tests
npx tsc -bclean,npx eslint .clean,npm run buildclean,ui/distrebuilt.Walked end to end against MiniStack
Whole lesson, headless, from a clean progress file: start, land on the right route, create the bucket from a terminal, watch auto-verify pass it with nothing clicked, open the bucket, upload from the terminal, watch that pass too, finish. Server-side progress ends with all five steps recorded, and the completion screen names the bucket that was actually created. Screenshots below.
AWS parity
Cloudscape is the console's design system, and
AnnotationContext/Hotspot/ the tutorial model are the same components AWS ships for its own in-console hands-on tutorials. They are used as intended: panel docked in the layout's right rail, tutorials started by an explicit click, hotspot popovers anchored on real controls. Live verification is our addition, and it is one AWS cannot offer, because their version runs against the learner's real account.Lesson copy was written against the docs rather than from memory (S3 welcome, bucket naming rules, uploading objects, HeadObject), all original prose, each step linking the AWS page for its concept. Every command is a real API call:
s3 ls,s3api create-bucket,s3 cp,s3api head-object, with--endpoint-urlsubstituted from the active endpoint rather than hardcoded, which a test enforces.Deliberately out of scope
Lessons are hard-blocked against real AWS, since they create and delete resources. The AI tutor stays out until real beginners have gotten stuck in real places. And the rest of the trail waits until this lesson has been played with, so the format can be judged before forty minutes of content gets written against it.