H
Hiroshi Matsumoto
Guest
Our juku — a Japanese cram school — opened in Osaka in 1989, and for more than thirty years I have watched the same child arrive every spring: not the one who is behind, not the one who is ahead, but the middle of the class. Fluent at 8 × 4, frozen the moment the multiplication is wrapped in a sentence. Kids don't fail maths. They fail reading.
What goes wrong is attention. A child reads the problem once, top to bottom, and believes they have read it; but only what attention actually catches reaches awareness, and the rest slips past. The children who stall have made that quick read a habit. So the fix we teach is physical, and it is aimed at attention, not at explanation. The child cuts the sentence into chunks with slashes, marks matching quantities in matching colours (six of them), one piece at a time, until everything the problem needs has been consciously noticed, and then redraws the whole problem as rectangles. Once the words are an area, the answer is already on the page. This summer I turned that method into an app, Aha! Math — Word Problems, and entered it in Shipaton. It went live on the App Store on 16 September after three rejections and one "information needed". This is the log, written for whoever is about to submit their own.
The colours did not start in maths. They started in Japanese reading class, with the students who were hardest to move: the ones who skipped the passage, read only the question, and wrote something down. Nothing I said changed that, so I tried something mechanical instead. Before answering, mark every character in orange, every place in purple, every time expression in green. That is all. It cannot be done without reading the passage, and it gives the eye somewhere to go. Students who had never read to the end started reading to the end. Scores followed. And a line I still hear from parents, at enrolment briefings and in parent-teacher meetings: they had no idea there was a way to study like this. The maths app is that same discovery, applied to a different kind of sentence.
The app has had three bodies. The first was an AppSheet prototype: just enough to learn whether a ten-year-old would actually cut a sentence on a tablet. The second was built in Adalo, where the hint system went through trial and error — how much to show, when to stop, what a hint looks like when it refuses to give the answer. The third, the one in the store, is a Capacitor app (Vite + WKWebView) with RevenueCat for subscriptions, a Cloudflare Worker in front of Google Gemini for the AI tutor, and the code written by an AI coding agent (Claude Code) under my instructions.
Every hint diagram was drawn by hand in Illustrator, one problem at a time. That turned out to be the real curriculum work: for each problem I had to decide what picture the sentence could become and which picture a child would carry to the next problem. Many of the diagrams behind the Hint button are not the ones a textbook answer would use. A tennis problem where a loss costs two points is drawn with the losses as area below a line; slide the pieces together and the whole match is one rectangle, and the answer falls out of its width.
A teacher directing an AI that writes code needs rules, and mine were mostly prohibitions:
The last rule exists because of what follows.
An automated message, the same evening: guideline 3.1.2. An app that sells auto-renewable subscriptions must link to its Terms of Use (EULA) in the App Store description, and mine didn't. I added Apple's standard EULA link to the description and resubmitted the same build. Two days later a human reviewer came back with three findings inside the app itself.
Apple named three things:
The plan bug was not in my code. It was in RevenueCat's dashboard: the offering's four packages had only the Google Play products attached. I had shipped Android first, and the App Store products were never linked. RevenueCat returns only the products that match the device's store, so on iOS
The forced login was a design decision I had defended for a long time and undid in an afternoon: a guest mode, scores stored under
The dead link is the interesting one. It used
While 143 was waiting for review, I read the source cold instead of waiting. Three things had survived:
I pulled 143, shipped 144 with all three fixed, and added a test that fails the build if
Apple asked for two things: how a user buys after the free period ends, and credentials for an account whose free period has expired. There are no such credentials. There is no server and no server-side account; the 14 days start from a timestamp in the device's local storage. I answered with what a reviewer can actually do: the plans are reachable during the free period from "Plans & Subscription" on the home screen, and to see the locked state, advance the device clock by fifteen days. The same sentence is now in the notes for the Shipaton judges.
Two operational traps here, both undocumented: the reviewer notes field is capped at 4,000 characters and the save button simply greys out above that; and after a rejection the four subscription products stay in "rejected" state separately from the build — you have to open each one and click "update review contents" before the resubmit button comes back.
Build 144 was rejected under 2.1(b) again: "attempted to purchase, got an error" — on an iPad Air running iPadOS 27 in compatibility mode.
Here RevenueCat did something for me that Apple's rejection could not. In Customers, I found the reviewer's session: iOS 27.0, SDK 5.83.0, US storefront, 14:56 UTC on the 11th. The record showed the app opening and the offering loading — and no purchase attempt, no receipt, no error. Whatever failed, failed inside StoreKit before it ever reached RevenueCat. Apple's screenshot confirmed the plans had rendered with USD prices; the status bar showed a VPN. I could not reproduce it: the only iPad in the building is a first-generation one, stuck on iOS 5.
I could not fix a bug I couldn't see, so I fixed the things around it: updated the RevenueCat SDK (13.4.0 → 13.5.1), replaced the generic failure string with error-code-specific text (an "Ask to Buy" request from a child's device is not a failure), kept the numeric code visible in the default message so the next screenshot from Apple would contain it, and turned on debug logging. Resubmitted 12 September. Approved 16 September, live the same morning.
Play rejected the app on 9 September for a reason I already knew: the reviewer landed on a login screen. Google does not create accounts during review, so a note with credentials does not help. The guest-mode build fixed it, and then an audit found one more route to that login screen — a "Logout" tab still visible to guests — which cost another build. Resubmitted 13 September, still in review as I write.
Every install gets 14 days of everything, granted by the app itself, not a StoreKit introductory offer. No card, no account, no charge at the end. A parent deciding whether to trust an unknown developer with their child is the buyer; one surprise charge ends that relationship.
Then four durations, all unlocking the same content: 1 month ¥300, 3 months ¥800, 6 months ¥1,500, 12 months ¥2,500 (roughly $1.99 to $14.99). Japanese families don't plan in "monthly vs annual." They plan in school terms and in the runway to a February entrance exam. Three months is a term.
After the Shipaton paywall livestream, in the Shipaton Discord, Julie from RevenueCat looked at one screenshot and found five things I had stopped seeing: no plan recommended, so the default was the cheapest; no per-month price, so the 31% saving on the year was invisible; the referral card ("give a friend three months free") sitting in the most valuable spot on the screen; nothing about what the child gets or what stops on day 15 — "all price and no value"; and a 3-month plan that saves 11% and therefore barely justifies its existence. Four of those go into the next build. The 3-month plan stays, for the reason above, until the conversion data says otherwise. RevenueCat carries all of it: one
The marking part of the method was studied in a doctoral thesis at Kyoto Institute of Technology (Nishimura Hiroki, 2017). Nishimura is the president and COO of our company, so this is our own research, not an outside evaluation. The full text is open on the university repository, in Japanese:
https://kit.repo.nii.ac.jp/records/2000279
In the experiment closest to the app, juku students in grades 4 to 9 answered the same 50 English grammar questions twice in a row, each time deciding whether a verb needs the third-person "s". Group A (28 students) worked without marking the first time and marked the subject in yellow the second time; their average went from 27.82 to 43.00. Group B (26 students) never marked; they went from 38.19 to 37.46. The thesis reads this as marking raising attention, so the point of the sentence is actually noticed, and B's small drop as fatigue.
The honest version: Group A started lower, and I could not find a significance test for this experiment in the thesis. The subject was English grammar, not maths, and the thesis studied the marking method, not this app. These are means from one experiment, not proof. I would rather readers heard that from me.
RevenueCat's overview on 16 September: 1 paid subscriber, $2 total revenue. That subscriber was me — a real purchase from a non-developer Apple ID on launch morning, because I wanted to watch production billing complete once before a parent did.
The same week, the app went in front of our own students in class, paper on the desk, tablet beside it. Two handwritten notes, translated: "It was really hard at first, but once you get used to it, it's easy. It's like a workout for the brain, and it was fun." "If you know a bit of English, you can tell what it wants, so it was fairly easy." And one shorter verdict I keep on the wall: "Lots of problems. Hard."
I started posting on 7 September, later than almost everyone, and every exchange since has changed something in the submission. A reader asked what happens when a child cuts the sentence in the wrong place — the answer (a "Redo the split" button and a grader that scores grouping, not colour) was built but had never been said out loud; it is now in the Design answer. A builder of interactive fiction argued the same rule from the other side across a week of replies: if the reader only watches, nothing sticks; push too hard and they close the app. And the developer of an IPA-scanning tool, after I described the
The app is on the App Store now. The thesis is open on the university repository, in Japanese. The classroom is where the next version gets decided. If you are shipping your first app for Shipaton and something above saves you a week, it was worth the three rejections.
Aha! Math — Word Problems on the App Store
The Shipaton entry on Devpost
What goes wrong is attention. A child reads the problem once, top to bottom, and believes they have read it; but only what attention actually catches reaches awareness, and the rest slips past. The children who stall have made that quick read a habit. So the fix we teach is physical, and it is aimed at attention, not at explanation. The child cuts the sentence into chunks with slashes, marks matching quantities in matching colours (six of them), one piece at a time, until everything the problem needs has been consciously noticed, and then redraws the whole problem as rectangles. Once the words are an area, the answer is already on the page. This summer I turned that method into an app, Aha! Math — Word Problems, and entered it in Shipaton. It went live on the App Store on 16 September after three rejections and one "information needed". This is the log, written for whoever is about to submit their own.
Where the six colours came from
The colours did not start in maths. They started in Japanese reading class, with the students who were hardest to move: the ones who skipped the passage, read only the question, and wrote something down. Nothing I said changed that, so I tried something mechanical instead. Before answering, mark every character in orange, every place in purple, every time expression in green. That is all. It cannot be done without reading the passage, and it gives the eye somewhere to go. Students who had never read to the end started reading to the end. Scores followed. And a line I still hear from parents, at enrolment briefings and in parent-teacher meetings: they had no idea there was a way to study like this. The maths app is that same discovery, applied to a different kind of sentence.
The stack nobody would choose on purpose
The app has had three bodies. The first was an AppSheet prototype: just enough to learn whether a ten-year-old would actually cut a sentence on a tablet. The second was built in Adalo, where the hint system went through trial and error — how much to show, when to stop, what a hint looks like when it refuses to give the answer. The third, the one in the store, is a Capacitor app (Vite + WKWebView) with RevenueCat for subscriptions, a Cloudflare Worker in front of Google Gemini for the AI tutor, and the code written by an AI coding agent (Claude Code) under my instructions.
Every hint diagram was drawn by hand in Illustrator, one problem at a time. That turned out to be the real curriculum work: for each problem I had to decide what picture the sentence could become and which picture a child would carry to the next problem. Many of the diagrams behind the Hint button are not the ones a textbook answer would use. A tennis problem where a loss costs two points is drawn with the losses as area below a line; slide the pieces together and the whole match is one rectangle, and the answer falls out of its width.
A teacher directing an AI that writes code needs rules, and mine were mostly prohibitions:
- The app never marks the sentence for the child. Deciding where to cut is the skill. There is a "sample marking" button, and it is only ever a sample.
- The grader checks grouping, not colour. Which colour a child chose is irrelevant — except blue, which is reserved for what the question asks; whether the matching quantities ended up in the same group is everything. That is a unit test, not a slogan.
- Hints come one diagram at a time, in order. The early ones stop at a picture the child still has to finish; on some problems, the final hints work the calculation through.
- No algebra from the tutor. Algebra is the shortcut that skips the picture.
- Nothing leaves the device until the child taps "Allow and continue" on a sheet that names the AI provider and lists exactly what is sent.
- A pre-ship inspection that opens the built bundle and compares its contents with the source tree, item by item, because an AI that "fixed it" and a bundle that contains the fix are two different claims.
The last rule exists because of what follows.
Rejection 1 (2 September): a link that wasn't there
An automated message, the same evening: guideline 3.1.2. An app that sells auto-renewable subscriptions must link to its Terms of Use (EULA) in the App Store description, and mine didn't. I added Apple's standard EULA link to the description and resubmitted the same build. Two days later a human reviewer came back with three findings inside the app itself.
Rejection 2 (4 September): three findings, and the one that mattered
Apple named three things:
- 2.1(b) — "Could not load plan information." The plans never appeared.
- 5.1.1(v) — the app forced the child to create an account before doing anything.
- 3.1.2(c) — the Privacy Policy link did nothing when tapped, and there was no Terms of Use link inside the app at all. The "Legal Notice" I had been treating as terms is Japan's statutory commerce disclosure — a different document.
The plan bug was not in my code. It was in RevenueCat's dashboard: the offering's four packages had only the Google Play products attached. I had shipped Android first, and the App Store products were never linked. RevenueCat returns only the products that match the device's store, so on iOS
availablePackages came back empty and the paywall showed its error string. Ten minutes in the dashboard. If you shipped Android first, check this before you upload anything to Apple.The forced login was a design decision I had defended for a long time and undid in an afternoon: a guest mode, scores stored under
guest, migrated when an account is created later.The dead link is the interesting one. It used
target="_blank". In Safari that opens a tab. In a WKWebView with nothing on the native side wired to handle it, it does nothing — a link that is alive on the web and dead in the app. I fixed the links Apple named — the bundled pages now open in an in-app sheet, the EULA goes to Apple's standard URL — and resubmitted as build 143.The audit that pulled build 143 from the queue
While 143 was waiting for review, I read the source cold instead of waiting. Three things had survived:
- Another
target="_blank"link, in the Professor Aha (AI tutor) panel. The same defect Apple had just cited. I had fixed the instances Apple named and never fixed the class. - No consent before sending a child's text to Gemini. The AI panel sent the question straight to the Worker. A footnote on the screen is not consent under 5.1.2(i), and I had told Apple in my notes that Gemini existed, so the reviewer was going to open that panel.
- The paywall heading said "Free trial." There was no introductory offer configured in App Store Connect. Fourteen free days are fine; calling something a free trial that the store doesn't know about is 3.1.2(a). It became "Free access: 14 days left."
I pulled 143, shipped 144 with all three fixed, and added a test that fails the build if
target="_blank" ever reappears. 825 tests passed. Ten days later, on RevenueCat's Shipaton livestream about App Review, the guests said the thing I wish I had known in August: on a resubmission, reviewers look at what you changed for the rejection. They don't dig through the whole app. That is exactly how a dead link survives. Do your own full pass before you resubmit — not the diff, the app."Information needed" (8 September): the account that doesn't exist
Apple asked for two things: how a user buys after the free period ends, and credentials for an account whose free period has expired. There are no such credentials. There is no server and no server-side account; the 14 days start from a timestamp in the device's local storage. I answered with what a reviewer can actually do: the plans are reachable during the free period from "Plans & Subscription" on the home screen, and to see the locked state, advance the device clock by fifteen days. The same sentence is now in the notes for the Shipaton judges.
Two operational traps here, both undocumented: the reviewer notes field is capped at 4,000 characters and the save button simply greys out above that; and after a rejection the four subscription products stay in "rejected" state separately from the build — you have to open each one and click "update review contents" before the resubmit button comes back.
Rejection 3 (11 September): the reviewer's device, seen from RevenueCat
Build 144 was rejected under 2.1(b) again: "attempted to purchase, got an error" — on an iPad Air running iPadOS 27 in compatibility mode.
Here RevenueCat did something for me that Apple's rejection could not. In Customers, I found the reviewer's session: iOS 27.0, SDK 5.83.0, US storefront, 14:56 UTC on the 11th. The record showed the app opening and the offering loading — and no purchase attempt, no receipt, no error. Whatever failed, failed inside StoreKit before it ever reached RevenueCat. Apple's screenshot confirmed the plans had rendered with USD prices; the status bar showed a VPN. I could not reproduce it: the only iPad in the building is a first-generation one, stuck on iOS 5.
I could not fix a bug I couldn't see, so I fixed the things around it: updated the RevenueCat SDK (13.4.0 → 13.5.1), replaced the generic failure string with error-code-specific text (an "Ask to Buy" request from a child's device is not a failure), kept the numeric code visible in the default message so the next screenshot from Apple would contain it, and turned on debug logging. Resubmitted 12 September. Approved 16 September, live the same morning.
Google Play, meanwhile
Play rejected the app on 9 September for a reason I already knew: the reviewer landed on a login screen. Google does not create accounts during review, so a note with credentials does not help. The guest-mode build fixed it, and then an audit found one more route to that login screen — a "Logout" tab still visible to guests — which cost another build. Resubmitted 13 September, still in review as I write.
Pricing: what a Japanese family actually budgets in
Every install gets 14 days of everything, granted by the app itself, not a StoreKit introductory offer. No card, no account, no charge at the end. A parent deciding whether to trust an unknown developer with their child is the buyer; one surprise charge ends that relationship.
Then four durations, all unlocking the same content: 1 month ¥300, 3 months ¥800, 6 months ¥1,500, 12 months ¥2,500 (roughly $1.99 to $14.99). Japanese families don't plan in "monthly vs annual." They plan in school terms and in the runway to a February entrance exam. Three months is a term.
After the Shipaton paywall livestream, in the Shipaton Discord, Julie from RevenueCat looked at one screenshot and found five things I had stopped seeing: no plan recommended, so the default was the cheapest; no per-month price, so the 31% saving on the year was invisible; the referral card ("give a friend three months free") sitting in the most valuable spot on the screen; nothing about what the child gets or what stops on day 15 — "all price and no value"; and a 3-month plan that saves 11% and therefore barely justifies its existence. Four of those go into the next build. The 3-month plan stays, for the reason above, until the conversion data says otherwise. RevenueCat carries all of it: one
premium entitlement gates every screen, so pricing and packaging change in the dashboard, not in a binary that has to survive App Review.The evidence, with its weak spot on the outside
The marking part of the method was studied in a doctoral thesis at Kyoto Institute of Technology (Nishimura Hiroki, 2017). Nishimura is the president and COO of our company, so this is our own research, not an outside evaluation. The full text is open on the university repository, in Japanese:
https://kit.repo.nii.ac.jp/records/2000279
In the experiment closest to the app, juku students in grades 4 to 9 answered the same 50 English grammar questions twice in a row, each time deciding whether a verb needs the third-person "s". Group A (28 students) worked without marking the first time and marked the subject in yellow the second time; their average went from 27.82 to 43.00. Group B (26 students) never marked; they went from 38.19 to 37.46. The thesis reads this as marking raising attention, so the point of the sentence is actually noticed, and B's small drop as fatigue.
The honest version: Group A started lower, and I could not find a significance test for this experiment in the thesis. The subject was English grammar, not maths, and the thesis studied the marking method, not this app. These are means from one experiment, not proof. I would rather readers heard that from me.
Launch day, by the numbers
RevenueCat's overview on 16 September: 1 paid subscriber, $2 total revenue. That subscriber was me — a real purchase from a non-developer Apple ID on launch morning, because I wanted to watch production billing complete once before a parent did.
The same week, the app went in front of our own students in class, paper on the desk, tablet beside it. Two handwritten notes, translated: "It was really hard at first, but once you get used to it, it's easy. It's like a workout for the brain, and it was fun." "If you know a bit of English, you can tell what it wants, so it was fairly easy." And one shorter verdict I keep on the wall: "Lots of problems. Hard."
What building in public actually changed
I started posting on 7 September, later than almost everyone, and every exchange since has changed something in the submission. A reader asked what happens when a child cuts the sentence in the wrong place — the answer (a "Redo the split" button and a grader that scores grouping, not colour) was built but had never been said out loud; it is now in the Design answer. A builder of interactive fiction argued the same rule from the other side across a week of replies: if the reader only watches, nothing sticks; push too hard and they close the app. And the developer of an IPA-scanning tool, after I described the
target="_blank" bug, added it as a check the same day — "that one came from you." Two more followed over the next five days, each from a question I asked about one of our rejections: scan every .html and .js in the bundle rather than only the main entry, and flag "free trial" copy when App Store Connect has no introductory offer configured. Three of our rejections are now line items in someone else's pre-flight scanner. That is the best thing a rejection can turn into.The checklist I wish I'd had
- Android first? Attach the App Store products to your RevenueCat offering before the first iOS build. Empty packages look like a code bug and get you a 2.1(b).
- Grep the whole bundle for
target="_blank"if you ship a web view. Add a test so it stays at zero. - Fix the class, not the instances. Reviewers check your diff; you have to check the app.
- If a child's text goes to a third-party AI, put a consent sheet in front of it that names the provider. A footnote is not consent.
- Don't call it a "free trial" unless App Store Connect has an introductory offer. "Free access" is fine.
- No server accounts? Tell the reviewer how to see the locked state (advance the clock) instead of inventing credentials.
- After a rejection, reset each subscription product separately or the submit button stays grey.
- Keep the reviewer notes under 4,000 characters. The field fails silently.
- When a purchase "errors" in review, open RevenueCat → Customers first. If the attempt never arrived, the bug is in StoreKit, not your integration — and you can say so in your reply.
- Test on an iPad, even if the app is iPhone-only. That is where they review it.
The app is on the App Store now. The thesis is open on the university repository, in Japanese. The classroom is where the next version gets decided. If you are shipping your first app for Shipaton and something above saves you a week, it was worth the three rejections.
Aha! Math — Word Problems on the App Store
The Shipaton entry on Devpost