Google Summer of Code 2026 · MIT App Inventor
Connecting MIT App Inventor to the Classroom
The goal
A teacher who brings App Inventor into a class meets the same three walls every term. Every student should start from the same template project. The finished work has to come back without a hundred email attachments. The grade belongs in the system the school already runs. App Inventor has tens of millions of users and no concept of a class, an assignment, or a grade.
The goal of this project was to let the systems that already manage classes drive App Inventor, so that a teacher can assign App Inventor work from their learning management system, a student can open it with one click and land inside the IDE signed in and on their own copy, and the grade can flow back to where teachers expect it.
The first route, Google Classroom
The project began with Google Classroom, since it is free, widely deployed, and most App Inventor users already hold a Google account. This phase produced a complete sign in with Google using the OAuth authorization code flow with PKCE, the refresh token encrypted at rest with the server’s existing key infrastructure, only a read only Classroom scope requested, and everything off by default behind server properties. On top of that came the server side building blocks for exporting a project to a student’s Drive and for handing work in to Classroom. This exploration lives in pull request 4007.
It also taught the most important lesson of the summer. The flow works, and it reaches exactly one platform. Schools arrive with Moodle, Canvas, Schoology, or Classroom, and a per vendor integration starts over with every one of them.
The pivot to LTI 1.3
LTI 1.3 is the interoperability standard the learning management world already speaks, maintained by 1EdTech and supported natively by every major platform. One implementation, and any of them can launch students into App Inventor and receive submissions and grades back. Signed messages and published key sets replace shared secrets, and every launch is single use. With my mentors I decided to implement the standard rather than the vendor, and the rest of the summer went into building it. The one platform the standard does not reach is Classroom itself, which does not speak LTI, so the building blocks from the first route remain the path for a Classroom school, kept as a first chapter rather than as discarded work.
What was built
- A complete LTI 1.3 tool inside the App Inventor server. OIDC login initiation, launch validation against the platform key set covering the signature, the issuer, the audience with the authorized party, the protocol version, the message type, a one time nonce, expiry, and the deployment id, plus a public JWKS endpoint for the platform to verify the tool’s own messages.
- Accounts nobody has to think about. A launch provisions an App Inventor account in a namespace that ordinary sign ins can never reach, derived from the platform identity with a length prefixed hash so two identities cannot collide into one account. Students never see a password.
- One project per learner per assignment. The launch copies the teacher’s template into the learner’s account, names it after the activity, and finds the same project again after a rename, a server restart, or a launch on another instance. Every piece of state lives in the datastore.
- Template selection through Deep Linking, held steady by design. The chosen project is frozen into a reserved account at selection time, so later edits by the teacher never leak into the assignment. The specification forbids a Deep Linking request from naming the link it replaces, so the tool cannot refuse a late change the way a closed system could. Instead the assignment is fixed to one template by the first learner who opens it, which keeps a whole class on one starting point without breaking the rule.
- Submit to LMS from the Project menu. The item appears only on assignment projects, waits until everything the window is sending has reached the server, and then freezes a copy of the project into a reserved account. The platform is told a submission arrived only after that copy is stored and read back, so the gradebook never shows work a teacher cannot open.
- Grades where teachers expect them. Grade passback uses the Assignment and Grade Services score endpoint with the states Submitted and PendingManual, so the tool reports arrival and the teacher grades in the LMS.
- A read only review for the teacher. The tool answers the Submission Review message, checks that the assignment link, the recorded submission, and the frozen copy all belong to the learner being reviewed, and then opens the frozen copy read only. It never opens the learner’s live project, because that account holds their work for every course on the platform.
- Registration by URL behind a flag. An administrator can register App Inventor with a platform by pasting one address, and a newly registered platform is stored disabled until an administrator enables it.
- A testable save coordinator in the client. What is saved and what is still on its way to the server is decided by one small browser free class, so the rules that protect a submission can be proven by plain unit tests rather than trusted by reading.
- A reference page under docs. Registration steps for Moodle and Canvas, the server flags, a local Moodle walkthrough for development, and sixteen known limitations named honestly before any production use.
How it was verified
The whole loop was exercised end to end against a real Moodle and a real Canvas running locally in Docker. A teacher creates the assignment and picks a template, a student launches, works, and submits, the teacher grades and opens the frozen copy, and the student sees the grade. The same code serves both platforms with no per platform branches.
At the close of the program the server suite finishes at 225 tests, the shared suite at 22, and the client suite at 42, with no failure and no error, on the same JDK the project’s continuous integration uses. The security guards are mutation tested, meaning each guard was removed in a copy of the code to confirm the matching test turns red, so the tests hold behaviour rather than mirror the code. The integration was also split into seven stacked pull requests that each build and pass their tests from a clean checkout, and the split is proven lossless because the final layer’s git tree hash equals the design branch’s tree hash byte for byte.
Where the code is
Everything below is public. The two extracted pull requests are open for review against the main repository, and the full integration stands as a design branch plus seven readable layers. The commit noted for each is the final commit of the program, so anything after it is post program work.
| Work | Where | State |
|---|---|---|
| Archive export overload | mit‑cml #4002 | Open, ready for review, at 5aa0bde70 |
| Submit to LMS client machinery with the save coordinator | mit‑cml #4063 | Open, ready for review, at fee496777 |
| Full LTI 1.3 design branch | mit‑cml #4031 | Draft on purpose, the standing reference, at 9583a2600 |
| The same work in seven layers, storage, plumbing, launch, submit, review, Deep Linking, documentation | #2, #3, #4, #5, #6, #9, #10 | Each layer builds and tests on its own, open for reading |
| Google Classroom exploration, sign in with PKCE, Drive export, hand in | mit‑cml #4007 | Draft, the first chapter of the story |
| Reference documentation with the known limitations | lti‑integration.md | On the design branch |
At the time of writing nothing has merged yet. The team chose to take the two extracted pull requests first, because the classroom portal work builds directly on them, and to keep the full LTI stack as a documented reference until there is bandwidth to take it to production.
What is left
The reference page names sixteen limitations, each written next to the capability it limits, so the next person can pick any of them up with the context already in place. The largest are binding the launch state to the browser that began the login, enforcing the read only review on the server rather than in the client, supporting several deployments under one registration, and sending the registration token the way Canvas expects during registration by URL. None of them blocks the development loop the page documents, and all of them stand between this spike and a real student pilot.
Challenges, and what they taught
- Standards constrain design in productive ways. The rule that a Deep Linking request may not name the link it replaces looked like an obstacle to keeping a class on one template. It became the design. The first learner fixes the template, the frozen copy holds it, and the constraint is satisfied without giving up the guarantee.
- Read the upstream code the way a reviewer would. The ordinary project export deliberately leaves out generated Yail, and the comment beside it explains that the Firebase component can leave a long lived token inside. Assignment copies cross account boundaries, so carrying everything would have carried that token from a teacher to a whole class. The fix went inside the copy service itself, which now works out on its own when a copy crosses accounts, so every caller is covered, including one the classroom portal already uses.
- Tell the platform only what a teacher can open. Failure paths kept collapsing into one invariant. The gradebook may say a submission arrived only if the frozen copy behind it is stored and readable. Once that sentence was written down, every retry, timeout, and race had an answer.
- Testability is designed, not found. The client save logic runs inside a compiled web client that unit tests cannot start. Extracting the bookkeeping into one browser free class turned an untestable path into fourteen plain unit tests, and mutation testing keeps them honest. The few lines that hand the class to the editor are named in the tests as verified by reading, so the boundary of the proof is written down too.
- Trust artifacts, not checkmarks. The project’s test harness can report success while a suite fails, so every count in this report comes from parsing the JUnit XML, and the seven way split is accepted only because a tree hash says the layers add back up to the whole. Review findings were reproduced before they were accepted, on every round, and the claims that did not survive reproduction were corrected in public.
Acknowledgments
Thank you to my mentor, José Dominguez, for steering the pivot, for reviewing the work with care, and for the standing rule that shaped all of it, which is that a change must earn its place in a thirty five million user codebase. Thank you to the MIT App Inventor team and community for the codebase, the conversations, and the patience with a summer of questions.