Imported from jxbleo/jxbleo.github.io (
AGENTS.md). Install upstream withnpx skills add jxbleo/jxbleo.github.io. Copyright stays with the author.
Mr. Cat Academy Agent Guide
DSE topic bank direct reader (owner revision, 2026-10-17)
The owner explicitly removed the topic bank's old preview/login/iframe design. dse-topic-bank.html must show the complete Writing-handout-style reader directly without a session check or protected-resource request. This exception applies only to this topic bank; do not change JUPAS, other handouts, student data or shared authentication. A future common permission template is deferred until the owner designs it. Reuse the canonical shared header and preserve noindex/no-referrer.
Keep canonical topic content private under .cloudbase-private/handouts/source/dse-core-topics/. The direct output published.html is sealed with node scripts/prepare-topic-bank.js, which updates only that entry and preserves all other encrypted source and existing 39 handout identities. The root HTML is a build blueprint; static build must authenticate and validate the complete topic reader before replacing dist. Full teaching content is intentionally readable at the public topic URL, but plaintext authoring stays out of Git.
Earlier Speaking toolbar rollout (superseded by reusable-header rule below)
Supersedes the earlier parent-reader letterhead: place a handout-style toolbar above the report card, with a cat-only index.html home link on the left and Xiaohongshu, Douyin, WeChat and email icons on the right. Reuse the approved handout destinations and interactions, accessible names and no-referrer links. The home link never forwards the share token. Keep the toolbar present through loading, ready and unavailable states; preserve all report/access behavior.
Reusable shared-reader header (owner rule, 2026-10-16)
All shared handouts and the parent Speaking report use the same cat + 进入主页
- four-contact toolbar. Future share readers must reuse
scripts/share-header.jsandassets/css/share-header.css/assets/js/share-header.js, not copy a new implementation. Use a markeddata-share-headerheader and runnode scripts/sync-share-headers.js; the static build renders marked readers too. Independent handouts consume the same template through their private-source renderer. Retain resource access/login controls and all authorization boundaries. The home label is 13px, regular weight, beside the cat on one row at normal mobile sizes. The legacy handout contact script remains for cached older pages only.
Independent DSE handouts (owner rule, 2026-10-16)
All published handouts label references 权威资料参考. Display institution /
document names and concise student/family-facing relevance descriptions as plain
text, without reference links. Preserve research URLs and access/evidence notes
privately; do not present internal reading logs or imply official endorsement.
Distribute each DSE Reading/Writing handout independently. The current scope is
39 share targets: one complete Writing 01–32 course, 32 separate writing
modules, four reading guides and two earlier writing guides. Each target has its
own stable 192-bit random filename. A complete-course hash is never a separate
handout. Student pages have no print, handout-copy or handout-share controls.
Owner revision (2026-10-16): the header cat and the 13px regular-weight text
进入主页 share one accessible index.html home link, opening
a new tab without referrers. This specific home entry is allowed; retain the
ban on peer-handout addresses and lesson-directory links. The header has owner-approved
icon-only Xiaohongshu and Douyin owner-profile links, a button that copies the
owner WeChat ID jxbleo_, and mailto:jxbleo@foxmail.com.
The owner verified that the Douyin share link can open the App from WeChat.
The Douyin icon now directly follows https://v.douyin.com/rjLsjpFg0as/ using
the same normal HTTPS anchor as that verified link. Do not intercept it with
clipboard copying, a local popup, a forced app scheme or extra opening steps.
WeChat copy success tells the visitor to search that ID in WeChat and add a friend.
Use the owner-provided Xiaohongshu share URL https://xhslink.cn/o/aYLtaYWoBd.
In WeChat, let that official page handle app opening; do not intercept it with
a forced scheme or an extra local instruction panel. No Video Channels entry or
visible contact labels. Keep accessible names and tooltips. Xiaohongshu mobile clicks
may attempt an app hand-off with an explicit HTTPS fallback; never guarantee
app detection or use timed redirects. Web links open a new tab without referrers;
never use these controls to expose another handout or platform lesson directory.
Keep copy/open controls in the private teacher directory. A standalone module must contain
only that module, its required task context and relevant references, without
other modules, course navigation, cross-handout promotional links or a lesson-directory entry;
the explicitly approved branded homepage link is the sole exception.
Canonical plaintext now lives only under ignored
.cloudbase-private/handouts/source/. Both writing/tones builders read and write
there. Keep real link maps, content keys, local link lists and generated private
teacher catalogs outside Git and public assets. Only authenticated AES-256-GCM
source in scripts/handouts.sealed.json may enter the public repository; use the
existing Node built-ins, not a new dependency. Never restore old root HTML or
content/study-guides/ plaintext to public output. Historical paths mentioned
elsewhere in this guide/docs are not current distribution links.
prepare-handouts.js --initialize preserves existing identities; --seal
refreshes the encrypted source; --teacher-catalog prepares the private teacher
directory and local share list. Do not rotate links during routine edits. The
static build must authenticate source and validate the entire unique map before
replacing dist; missing CI secrets must fail closed. Do not put random links
in build logs, public catalogs, public screenshots, source maps or public artifacts.
Every page must suppress indexing/referrers and contain no peer share address.
teacherHandoutLinks returns its bundled private catalog only after verifying
the current caller against an active teacher profile on every request. It adds
no database writes or collection. The Teacher UI must not persist the directory
or fall back to predictable legacy URLs. Public content/share-materials.json
must exclude these independent guides.
The owner-authorized initial cutover deployed the private teacher function,
published the scoped static release and retired the old COS paths. Future
secret changes, deployments and object retirement still require authorized
production work; consult the current deployment record before repeating them. The repository is public and
Pages is enabled; already-public Git history is not erased by renaming pages.
See docs/research/dse-handout-isolation-2026-10-16.md and current deployment
instructions. Do not ask the owner to hand-edit JSON or link maps.
Shared Speaking and Writing text models (owner rule, 2026-09-13)
Speaking and Writing are one text-model configuration scope. Unless the owner
explicitly asks to separate them, every text-model change applies to both
speakingLab and writingTutor, including their ordered quota fallback list.
The canonical policy is scripts/text-model-policy.json: currently
qwen3.8-max first, then qwen3.8-max-0902. Do not leave either text
provider on Plus, and never place the primary model in its fallback list.
Use node scripts/configure-text-models.js for a safe paired dry run,
--apply only with owner authorization, and --check to verify both live
functions. Never report completion after checking only one function. Preserve
OCR, speech recognition, voiceprints, credentials, provider protocols, output
limits, free-only billing switches, jobs and historical reports. This rule
unifies text-model identity and fallback order, not the two tasks' prompts or
schemas. If a cloud update is interrupted, run --check before rerunning
--apply; the two CloudBase function updates are not an atomic transaction.
1. Project Intent
Teacher AI Usage invariant (2026-09-16)
AI Usage is an active-teacher-only, read-only metadata projection of existing Writing/Speaking ledgers and Scan Words/legacy Writing tasks. Preserve unknown Token/call counts as unknown, label task aggregates and group creator attribution, and distinguish estimated API value from the actual invoice/free-quota balance. Keep bounded history pages outside Teacher bootstrap and persistent browser caches; never expose prompts, answers, audio, credentials or raw authentication UIDs. Changes here do not authorize model/billing changes or production deployment.
Mr. Cat Academy is a static learning website with a CloudBase backend. The owner should be able to give an agent Markdown, PDFs, raw questions, or natural-language changes and have the agent turn them into complete website content without asking the owner to hand-edit JSON, database records, or links.
The finished system lets the owner manage students from the teacher page, assign work to individuals or classes, grade on the server, and retain every countable attempt.
2. Read These Sources First
Use this order when learning the project:
AGENTS.md: binding implementation rules and invariants.README.md: current project entry point, commands, and document map.docs/01_PRODUCT_REQUIREMENTS.md: human-readable product requirements.docs/02_ARCHITECTURE.md: current architecture and data flow.docs/04_DATA_MODEL.md: CloudBase collections, fields, statuses, and ownership.docs/11_AGENT_TROUBLESHOOTING.md: repeated technical issues and diagnosis notes.docs/09_CONTENT_WORKFLOW.md: how teaching material becomes website content.docs/10_DEPLOYMENT.md: current deployment and import procedure.- Existing code and data: the final source of truth for current behavior.
If documentation conflicts with working code, investigate before changing either. Preserve confirmed product rules, then update stale documentation in the same change.
Documentation Update Rules
- Product behavior or business rules: update
docs/01_PRODUCT_REQUIREMENTS.md. - Architecture, backend boundaries, or directory structure: update
docs/02_ARCHITECTURE.md. - Page layout or interaction behavior: update
docs/03_UI_UX_SPEC.md. - Collections, fields, statuses, ownership, or data flow: update
docs/04_DATA_MODEL.md. - Product-level or architecture-level changes: update
docs/05_CHANGELOG.md. - Important technical choices or new dependencies: update
docs/06_DECISIONS.md. - Changed manual/automated test expectations: update
docs/07_TESTING_CHECKLIST.md. - Known bugs, future work, or technical debt: update
docs/08_BACKLOG.md. - Content import/source workflow changes: update
docs/09_CONTENT_WORKFLOW.md. - Deployment, CloudBase import, or environment procedure changes: update
docs/10_DEPLOYMENT.md. - Repeated technical failures or diagnosis lessons: update
docs/11_AGENT_TROUBLESHOOTING.md.
Every meaningful change should end with a short note covering files changed, behavior changed, docs updated, tests run, and remaining risks or owner actions.
Do not modify unrelated files, clean the working tree, or introduce new
dependencies unless the task truly requires it. Any new library, framework,
service, or architecture choice must be recorded in docs/06_DECISIONS.md.
Deployment automation is owner-gated. Agents may run release verification, function packaging, and deploy-plan generation. Agents must not run CloudBase deploy commands, request Tencent Cloud secrets, or change CloudBase production resources unless the owner explicitly authorizes that exact action.
3. Ownership and Safety
The Tencent Cloud account, CloudBase environments, billing, domains, and production authority belong only to the project owner.
Never commit or hard-code:
- Tencent Cloud
SecretIdorSecretKey - administrator credentials, access tokens, or refresh tokens
- private keys or service-account files
- student passwords
- the initial/reset password
- private grading keys, answers, accepted variants, or explanations
The public browser configuration may be committed:
environment: mrcat-dev-d9gwy2v1icdfdf597
region: ap-shanghai
timezone: Asia/Shanghai
Do not weaken database permissions. The collections below must remain
ADMINONLY. Do not modify production resources, billing, DNS, domains, or
account settings without explicit owner approval.
4. Repository Map
Important entry points:
index.html: login and visitor entrydashboard.html: student dashboardmy-words.html: authenticated student My Words study/list workspaceteacher.html: separate teacher interfacereports.html: authenticated shared weekly/monthly learning-report readerbbc.html,ielts-reading.html,vocabulary.html: practice runtimesassets/js/cloudbase-client.js: browser CloudBase wrapperassets/js/practice-session.js: practice submission integrationassets/js/dashboard.js: student dashboard behaviorassets/js/teacher.js: teacher interface behaviorcontent/: canonical metadata and vocabulary source datadata/: browser-readable exercise data and generated catalogscripts/: content import, catalog, and private-data preparation toolscloudfunctions/: cloud function sourcedeploy-packages/: generated deployment ZIP files, not source
Do not make a new permanent standalone HTML page for each exercise. Reuse the current schemas and shared practice pages. Temporary classroom pages may stay standalone when the owner requests that.
BBC standalone page lessons learned
Some older BBC 6 Minute English classroom pages live as standalone root-level
HTML files, for example 250904-the-joys-of-writing-lists.html and
250724-what-is-degrowth.html. If the owner explicitly asks to match these
existing HTML pages, keep their current Formspree-style layout and behavior
instead of moving the task into the shared bbc.html runtime during that same
change.
Useful pitfalls from the 2025 BBC batch import:
- Source Markdown may be outside this repo in the owner's Obsidian/iCloud
folder. Quote paths with spaces, for example
/Users/leoji/Library/Mobile Documents/iCloud~md~obsidian/Documents/jxbleo/BBC 6 Minute English/.... - The Markdown files can already contain all exercise content: fill blanks, answer-key table, multiple-choice questions, and Chinese explanations. Prefer converting that structure directly over rewriting questions by hand.
- Fill-blank pages should preserve the existing pattern:
Blank_01throughBlank_20,Question_21throughQuestion_30, one student-name field, Formspree submit, and answer explanations revealed only after submission. - Be careful with nested bullets. The nested
<ul>must be inside the parent<li>; otherwise browsers may still render it, but the HTML is invalid and future styling can break. - Verify generated pages mechanically: count blank inputs, multiple-choice
blocks, explanation blocks, and ensure every
index.htmlBBC card links to an existing file. - One known source typo:
250821 Whats your favourite snack.mdhad MC Question 27's explanation labelledD, but optionCis the correct answer. Fix the generated page to showQuestion 27: C. - Browser verification with the in-app browser may block
file://URLs. Start a local static server and test throughhttp://127.0.0.1:<port>/index.htmlinstead. - A
git pushcan time out locally even after another terminal succeeds. Check withgit log --oneline --decorate -5and comparegit rev-parse HEAD origin/main; matching hashes mean the push reached GitHub.
BBC Intensive Listening timestamped-JSON intake
When the owner supplies a BBC timestamped transcript JSON, treat each supplied
segment boundary as the starting Intensive Listening unit. Preserve its
millisecond start/end values and normalize it into the self-contained source
format used by scripts/import-intensive-listening.js. Before publication,
mark the fixed programme ident, presenter greeting/self-introduction, website /
transcript / worksheet / quiz promotion, and closing goodbye sequence as
practiceMode: "skip"; preserve real programme discussion and real quiz
questions as dictation. Review the resulting Skip list instead of relying on
filename or position alone, because BBC wording and ASR splitting vary.
For every completed material, retain both owner backups under iCloud:
猫先生英语/BBC/Scripts JSON/IL-BBC-<date>.json: complete machine-readable source猫先生英语/BBC/Scripts MD/IL-BBC-<date>.md: readable source with timestamps, speaker, and practice mode
MP3 remains outside this backup. Import only the intended IL-BBC-* records
into the ADMINONLY CloudBase material collection and link the existing BBC
lesson card; never publish full transcript answers in static data/.
5. Authentication Has Two Linked Layers
A working student account always has both:
- a CloudBase Authentication username/password end user
- a top-level document in the
studentscollection
The link is the authentication user ID stored as auth_uid. student_id is
the human-facing Login ID. These are not interchangeable.
Canonical student profile:
{
"auth_uid": "CLOUDBASE_AUTH_USER_ID",
"student_id": "unique-login-id",
"name": "Chinese NameEnglish Name",
"chinese_name": "Chinese Name",
"english_name": "English Name",
"class_group": "",
"curriculum_track": "",
"role": "student",
"active": true,
"must_change_password": true,
"created_at": "Date",
"updated_at": "Date"
}
chinese_name and english_name are the authoritative editable parts. The
trusted backend derives compatibility name as ChineseEnglish, or the one
available part. A legacy unsplit name is a whole display fallback only; never
heuristically split it by spaces or character ranges.
All fields must be top-level. With @cloudbase/node-sdk, add documents using:
await db.collection("students").add(student);
Never use add({ data: student }). That creates a nested data object and
causes Profile incomplete, failed lookups, and apparently missing accounts.
The same top-level direct-write rule applies to assignments and attempts.
An immutable attempt that needs an atomic idempotency key may use
collection.doc(stableId).create(attempt); keep the attempt fields top-level,
and never replace that create with set, which could overwrite history.
Login ID uniqueness
student_id is unique among current, non-deleted profiles; student names may
repeat. A deleted profile keeps its original Login ID only in
deleted_student_id_snapshot and replaces student_id with an internal
__deleted__:<profile-id> archive key so the teacher can deliberately create a
new account with the former Login ID.
Before creating an account, check both:
- non-deleted
students.student_id - CloudBase Authentication end-user username
If either exists, return STUDENT_ID_EXISTS. Do not auto-merge, auto-delete,
or silently repair current duplicate accounts. Reusing a Login ID after a
completed teacher deletion creates a new auth user and a new profile; it never
reattaches the deleted account's assignments, attempts, STAR records, or Argue
history to the new auth_uid.
Creating a student
The teacher page calls teacherAdmin. The function:
- validates the authenticated teacher
- checks Login ID uniqueness in both layers
- creates a CloudBase Authentication end user
- resolves its UID and enables it
- creates the matching top-level
studentsdocument - verifies the profile exists
- deletes the new auth user if profile creation fails
Use the @cloudbase/manager-node end-user APIs:
createEndUsermodifyEndUsersetEndUserStatusdeleteEndUsersgetEndUserList
Do not replace them with unified createUser. The website signs in with
CloudBase username/password end users; the wrong management API can create an
account visible in the console that cannot use the web login.
The initial password comes only from the INITIAL_STUDENT_PASSWORD cloud
function environment variable. Never put it in frontend code or Git.
Teacher authority
A teacher also has a students document with role: "teacher" and
active: true. Every teacher action must derive the caller UID from
server-side authenticated context and require a matching active teacher
profile. Never trust a role, UID, or Student ID sent by browser code.
Password and account state
- Passwords are not stored in the database and cannot be shown to teachers.
- Correct a misspelled student name through the teacher profile editor; a name correction does not require deleting or recreating the login account.
- Reset changes the CloudBase auth password to the configured initial password,
enables the auth user, and sets
must_change_password: true. - After a successful teacher-initiated reset, the Teacher UI opens the same compact checkmark confirmation used by assignment success. It identifies the student Login ID and retains the configured initial-password copy.
- Every independent Teacher modal—including Notifications, Argue, Student lookup/details, assignment management, practice-entry confirmation, and success confirmations—locks the page behind it against wheel, trackpad, and touch scrolling. Only modal-owned scroll containers may move. Stacked modals keep the lock until the final layer closes, then restore the exact prior page position.
- Disabling/enabling a student updates both CloudBase Authentication status and
students.active. - Student password changes must use an authenticated CloudBase/server flow.
- CloudBase rejects weak passwords such as pure repeated digits, even at six or
eight characters. Keep frontend and
changePasswordvalidation aligned with the short complex pattern: at least 6 characters with uppercase, lowercase, number, and symbol.
6. Database Collections
All collections use ADMINONLY:
students: profiles and teacher/student rolessets: assignable exercise metadataassignments: assigned task instancesattempts: immutable countable submissionsteacher_attempt_email_events: private idempotent attempt-email outbox and delivery auditgrading_keys: private answers, explanations, and scoring rulessystem_config: defaults such as passing percentagestudent_set_achievements: permanent protected STAR recordsstar_reward_ledger: append-only Yellow STAR credit/reservation/redemption/refund entriesstar_redemption_requests: student Cash request state and audit snapshotsstar_redemption_evidence: private permanent Cash evidence metadataanswer_disputes: student single-question Argue requestsgrading_key_history: immutable teacher grading-rule revisionsstudent_vocabulary_items: student-owned saved words and phrasesvocabulary_lexicon: shared curated/ECDICT/external dictionary cacheclasses: stable teacher-managed class recordsclass_memberships: time-bounded student-to-class historylearning_reports: preview and immutable published learning-report snapshotsparent_view_sessions: hashed Parent Mode sessions and anti-enumeration login guardsintensive_listening_materials,intensive_listening_progress: private canonical Listening content and Dictation result history- Retired
listening_shadowing_progress,listening_shadowing_takes, andlistening_shadowing_usage: historical ADMINONLY records only; no runtime readers/writers or scoring routes remain after 2026-09-22 learning_activity_sessions: permanent server-accepted Listening effective time plus deterministic per-student lease rows; never stores answers, transcripts, audio, provider evidence, or pointer coordinates
Read exact schemas in docs/04_DATA_MODEL.md and current code before adding
fields. The numbered docs are the current handoff entry point. Documents under
docs/archive/ preserve history and must not override current rules or code.
Preserve these stable identifiers:
student_id: unique Login ID during a current account lifecycle; a completed deletion releases it while retaining the original value in the deleted profile's audit snapshotauth_uid: authoritative authentication linkset_id: stable content/exercise IDassignment_id: unique assignment instanceattempt_id: unique immutable submissionclass_id: stable class identity; never useclass_groupdisplay text as a keyreport_id: stable class-plus-period learning report identity
Do not use display names as keys. In the teacher UI, identify a profile row by
database document ID (profile_id/_id) and use auth_uid for backend
ownership. This prevents same-name records from being selected together.
Teacher attempt reports opened from the notification bell show mistake-focused
paper review for BBC and Vocabulary: correct questions are omitted, and BBC
wrong questions include the private answer explanation. Recorded Vocabulary
Cloze attempts identify Quiz versus timed Practice; Practice activity
attempts retain the selected group IDs and create teacher notifications, but
never count toward student completion. Learn inline practice is unrecorded.
Teacher startup returns progress separately and does not wait for complete
notification or Argue history. Notification summaries load newest-first in
ten-thread pages. The browser automatically continues only until every unread
thread is represented, then a two-request current-tab queue silently prefetches
each unread thread's authorized per-attempt details from top to bottom. Earlier
notification history advances in ten-thread pages as the teacher scrolls the
bell list; each Argue status still advances through Load 5 more. Full
per-question submitted answers, correct answers, group results, and explanations
still come from one authorized attempt_id request at a time, remain outside
the persistent Teacher IndexedDB cache, and are reused by notification cards and
paper reports. Do not put complete attempt history back into a bootstrap
response because CloudBase caps a function response body at 6 MB. The Teacher
bell and Argue header controls never show a spinner for these invisible queues.
Recorded BBC and Vocabulary attempts may also create private teacher-email
events after the immutable attempt is stored. BBC uses one fixed seven-minute
window anchored to the first submission; every recorded Vocabulary Quiz and
timed Practice event is immediately due. Vocabulary email #2 includes attempts
#1 and #2, email #3 includes #1 through #3, and so on. Email uses the same
assignment/self-study thread key, cumulative chart data, Shanghai timestamps,
threshold snapshots, and mistake-only answer comparison as the Teacher bell.
Email delivery is asynchronous and must never make a successful student
submission fail. It does not mark the bell read. SMTP credentials, recipients,
and timer tokens must never enter Git. SMTP transport credentials and timer
tokens remain CloudBase-only environment values. Recipient addresses belong to
the authenticated teacher profile's attempt_email_recipients array and are
managed only through Teacher Personal Center. Only entries with enabled: true
receive new mail; disabling all addresses skips newly due events instead of
backfilling them after a later re-enable. Multiple enabled teacher addresses use
BCC. Parent delivery requires a future student-to-guardian authorization map
and must never reuse the global teacher recipient projection.
7. Assignment Rules
The separate teacher.html interface has four capsules:
AssignLibraryStudentsArgue
Assign
The teacher can choose a visible set or filter by column/keyword to assign
multiple sets, search students, filter by class_group, select one or multiple
students, and set assignment parameters. Assignment parameters are configured
per selected task row and require an explicit due week stored as due_at at
that Shanghai-time week's Sunday 23:59:59, plus a passing percentage and an
explicit Earn STAR choice. created_at remains the assignment-creation audit
time. assigned_at is a deprecated compatibility mirror and must not drive new
week grouping or reminder behavior. New
assignments do not earn STAR by default; only when Earn STAR is selected for
that task row is mastery_percentage required and used for future
mastery/STAR upgrades.
For the same student and set_id:
- no previous assignment:
available, selectable - existing
not_done,failed, orto_do:in_progress; ordinary duplicate individual assignment is not allowed, but a complete Class Assign may select it so the server can reuse and promote that open individual record - previous
done,passed, ormastered:completed, visibly marked but selectable for reassignment - previous
cancelled: available for reassignment and not treated as open
The server revalidates this. A duplicate open individual assignment is skipped
even if the browser sends it. Exception: when the effective recipients cover a
complete Class, an existing open individual assignment is moved into that new
class batch, keeps its assignment_id, adopts the class assignment parameters,
and is promoted with the other class rows. Reassignment after completion creates
a new assignment and preserves the old one.
Teachers can edit selected assignment records after assignment by explicit
assignment_id selections. This is assignment-level editing, not a separate
class-standard layer. Editing may change the required due week (due_at),
passing_percentage, and mastery_percentage. Teacher View Wxx grouping and
week filters use due_at. The View parameter editor has exactly three direct
rows: Due week, Passing %, and Mastery %. Due week is a normal select; both
percentages use the scroll-wheel picker. Earn STAR is the only checkbox and
controls whether Mastery % is enabled. Saving a matrix task-column scope
updates every explicit assignment represented by the current class/individual
filter; saving from one student's detail updates only that assignment. The
footer pairs a red Cancel assignments action with Save changes;
cancellation always requires a second dedicated confirmation modal.
Teachers can soft-cancel any selected non-cancelled assignment, including
passed and mastered records, when they need to clean up assignment
participation or the Teacher View matrix after class membership changes.
Cancellation sets
status: "cancelled" plus audit fields such as cancelled_at and
cancelled_by_teacher_uid; never hard-delete assignment records. Cancelled
assignments are hidden from the student dashboard, rejected by submitAttempt
when an old assignment URL is used, and do not block future reassignment.
Cancellation changes only the assignment participation: immutable attempts,
set-wide Exercise Progress, completion history, and protected STAR records
remain intact. A later reassignment still initializes from the student's
historical global best and may therefore be immediately passed or mastered.
When mastery_enabled is false, Mastery % is not required and the stored
mastery threshold must not block a higher Passing % update.
failed remains the same open assignment. Try Again creates another attempt,
not another assignment.
Backend STAR claims do not block future assignment of the same set_id.
Students
The Students page defaults to a searchable list. Selecting a student shows:
- basic profile and class
- To Do, Failed, and Done counts
- assigned work
- recent attempts
- assign-class, reset-password, enable, and disable actions
The create form is hidden until Create Student Account is selected. Assigning
a class updates class_group.
Periodic learning reports
Weekly and natural-month reports use Shanghai-calendar boundaries. A preview is teacher-only and may refresh its calculated facts while preserving teacher comments. Once the period closes and the report is published, its snapshot is immutable; later submissions do not silently rewrite it.
Every student has at most one active class_memberships record. Ending or
changing a class keeps the old membership for report history. A student who
joins or transfers during a period may receive a personal report for that
period but is not ranked until a complete reporting period begins.
Only server-derived assignment_scope: "class" batches that cover every active
member of one class are Class Tasks. The browser cannot declare a partial batch
to be class work. Editing/cancelling every row of a Class Task keeps the scope
and applies the mutation transactionally; a partial mutation atomically
downgrades the whole task to individual scope before changing selected rows.
The class leaderboard ranks eligible members by Class Tasks
due in the report period and passed by that report's cutoff; equal completed
counts share a rank. Monthly reports may therefore include a later-in-month
completion that remains missed in an already-published weekly report.
Self-study is reported separately and never changes class rank. Do not average scores across BBC, Vocabulary, IELTS Reading, IELTS Listening, or other exercise families. Period comparison is an integer completed-item difference only.
reports.html?report=<report_id> is a reusable authenticated reader, not a
public report file. Active students may receive the real-name class leaderboard
plus only their own detail; active teachers may receive all student details and
edit preview comments. A report ID is a locator, never an authorization token.
Published snapshots must not contain answers, grading keys, passwords, full
attempt answers, Argue details, or another student's private detail.
The first release copies the report link/WeChat text for the teacher to send manually. Browser print CSS provides Save as PDF; do not create a permanent public PDF URL. Scheduled generation must be idempotent, derive periods from server Shanghai time, and require a CloudBase-only environment token. Timer configuration, collection creation, migration apply, and function deployment remain owner-gated.
Parent Mode
Parent Mode is a separate read-only family surface, not a parent account and
not student impersonation. Its entry requires one active student's exact
Chinese and English names together. A successful match creates a random,
device-bound seven-day session whose raw token is never stored in CloudBase;
only its hash is retained in parent_view_sessions. Failed-name guards combine
a hashed network/device key and must not reveal which name field matched.
The personal card has Overdue, This week, Upcoming, and Completed views.
Only countable server submissions drive its Unsubmitted, Not Qualified, and
Qualified states. Qualified self-study may appear under Completed; unqualified
self-study stays hidden. Parent Mode never tracks browser drafts or unfinished
answers. Task detail returns all score bars for the viewed child, but loads one
owned attempt's wrong answers at a time. Correct answers and explanations must
follow the same feedback-reveal boundary as the student surface.
The class card uses Class Tasks only. Tasks are rows; students are columns. The viewed child is the fixed first student column and peers sort by qualified-task completion, then by the average Best Score across the same period tasks with unsubmitted items treated as zero for this tie-break only. Peer cells may expose real names, task Best Scores, state, completion progress, and rank; they must never expose peer assignment locators, attempts, durations, answers, wrong questions, disputes, or self-study. Parent Mode has no write actions, teacher preview, teacher enable switch, or teacher revocation action.
8. Submission, Grading, and Status
All grading happens in submitAttempt, never in trusted browser logic. The
authoritative learning result is one Exercise Progress per
student_uid + set_id, derived from every countable attempt regardless of its
assignment_id. Assignment rows project that global best against their own
Passing/Earn STAR standards; assignment_id remains submission context and
audit history, not a separate score silo.
The server derives the student from authentication, loads the visible set
and private grading_keys, grades normalized answers, calculates a percentage,
records every countable attempt, and updates the linked assignment.
The general default passing/mastery percentages are 50 / 90. Vocabulary
sets default to 90 / 100, while BBC sets default to 80 / 95.
Set-specific passing_percentage and mastery_percentage values override
these family defaults. Passing means:
percentage >= passing_percentage
Countable submissions update every non-cancelled assignment for the same
student/set with the global latest/best summary and its resulting to_do,
passed, or mastered status. A strict best-score increase updates
best_improved_at; equal or lower retries remain history and do not move
FINISHED ordering. Vocabulary Quiz may improve indefinitely. BBC attempts after
the earliest answer-reveal/mastery lock remain history but cannot improve the
effective best. Assignment status is monotonic: once an assignment is passed, a
later lower-scoring attempt must not move it back to to_do; once it is
mastered, normal code must not downgrade it. cancelled is a terminal soft
withdrawal state for open assignments and should not be revived by normal
submission or regrade flows. Still store every later valid attempt and update
latest/best summaries separately.
Every countable attempt is immutable history and includes score, pass state,
answers, per-question results, attempt number, grading version, timing, and
assignment/resource context. Independent Resources work uses
assignment_id: null.
The student dashboard groups assignments into TO DO and FINISHED. Backend
statuses may still be to_do, passed, or mastered, but the student
Assignments screen intentionally merges passed/mastered work into FINISHED.
Countable self-study work also enters FINISHED once its best recorded attempt
meets the set's passing standard; it does not require a later teacher
assignment. Timed Vocabulary Practice activity attempts remain excluded.
Do not reintroduce separate PASSED / MASTERED dashboard tabs or the old
MASTERED one-week/one-month/all range selector unless the owner explicitly asks.
The backend retains all attempts.
New Yellow STAR records are backend records unique by student_uid + set_id.
A mastered assignment creates or repairs that set's protected Yellow STAR only
when mastery_enabled is true. Historical assignment-keyed duplicate Yellow
STARs remain valid and redeemable, but normal code must not create new duplicates.
claimStar may remain as an idempotent fallback, but the dashboard count and
wallet must not depend on localStorage or a purely frontend action. Once recorded:
- it can never be revoked or downgraded by normal code
- later failing attempts cannot remove it
- later passing-standard changes cannot remove it
- answer-key changes cannot remove it
- only the best attempt reference and best percentage may improve
Independent Library/Explore mastery uses an active, stable, non-redeemable Blue
STAR with assignment_id: null. If the same student already owns that set's
Yellow STAR, later self-study does not create another Blue STAR. When a teacher
later enables Earn STAR for the set, the backend compares the student's verified
historical best against that assignment's STAR Rate; a qualifying Blue STAR is
marked converted and linked to the protected Yellow STAR. If Earn STAR is off,
no comparison or conversion occurs. getDashboard may repair missing historical
STAR records from mastered attempts for both assignment and self-study work.
Yellow STAR spending never changes or deletes achievement rows. The wallet uses
append-only star_reward_ledger entries referencing explicit achievement_id
values. One student may have only one open Cash Request. Creating a request
reserves its Yellow STARs; teacher completion settles the reservation, while
student cancellation, teacher rejection, or seven-day expiry releases it.
Completed requests can only be corrected through an append-only STAR Refund.
Cash requests record no money amount or exchange rate.
Each Cash Request needs at least one private Evidence Photo before teacher completion. Evidence may be uploaded by the owning student or the active teacher, is stored privately in CloudBase Storage, and remains visible in request history. The browser receives only short-lived upload metadata and temporary read URLs; it never directly reads the ADMINONLY redemption collections.
Teacher assignment candidates are not disabled by previous STAR claims.
Feedback and retry
The current default is feedback_policy: "always". Preserve the design for a
future policy that reveals complete answers only after passing.
Try Again clears browser answers, grading marks, and explanations while
preserving prior attempt records in CloudBase.
BBC is the exception for immediate post-submit correction. Closing its result dialog keeps the submitted answers in place and automatically reopens only incorrect fill-blank and matching controls. Correct controls and every multiple-choice radio group remain disabled. A BBC multiple-choice answer is permanently scored from its first submitted choice; later attempts must keep that server-enforced first-choice lock, and the student UI does not need a lock icon. Each repeat submission is still a new immutable attempt.
Historical STAR review normally returns only the student's submitted answers and
a correct/incorrect flag. It must not return correct answers or explanations
unless the linked assignment has already recorded answer_revealed: true. After
answers have been revealed, history review may return the historical attempt's
correct answer and explanation for a collapsed, per-question Explain action.
Argue
Only a wrong question in a recorded attempt can be disputed. The authenticated
student may include an optional reason. Enforce one record per
attempt_id + question_id.
BBC teacher preview (owner revision 2026-09-30) directly accepts the current
answer with acceptBbcAnswer only after an in-page confirmation showing the
question and proposed answer. Cancel/Escape makes no mutation; focus Cancel by
default and block duplicate submission while updating. It must not ask the
teacher to return to Argue for approval. Keep the existing private add/history/upward-regrade flow and
idempotent retry. BBC inline editing saves public question overrides in sets
and explanations in private grading_keys, with transactional revision checks
and private history. Each 28×28 pencil toggles its editor; each editor has its own
Save. Collapsed unsaved drafts retain their inputs and show a red dot beside the
pencil. Saving one question preserves all other drafts. Never persist these
teacher drafts in browser caches.
Warn before leaving with unsaved edits. Live corrections are authoritative;
reconcile them before reimporting static content or regenerating worksheets.
Other practice runtimes retain the existing flow below.
Teachers can also submit Argue requests from teacher=1 practice preview after
showing answers. Teacher-originated disputes use requester_role: "teacher"
and may have attempt_id: null; they are for correcting grading rules, not for
the original practice attempt. Resolving a teacher-originated add or
replace still applies the grading-rule change to matching historical attempts.
Teacher decisions are:
keep: retain the original rulingadd: add the submitted answer to accepted answersreplace: replace the future correct answer with the submitted answer
add and replace update private CloudBase grading_keys, increment
grading_version, and append grading_key_history. Do not put corrected
answers back into public JSON.
After add or replace, teacherAdmin automatically scans historical
attempts for the same set_id and same question_id; any attempt whose stored
submitted answer exactly matches the newly accepted answer is regraded upward.
This applies to student-originated and teacher-originated disputes, including
teacher preview disputes with attempt_id: null. Never downgrade historical
attempts, assignment status, or protected STAR records. If an adjusted attempt
reaches mastery, create or improve its protected STAR.
The one-time teacherAdmin.backfillAcceptedAnswerRegrades action exists for
old grading-rule changes that happened before automatic Argue regrading. It
requires an authenticated active teacher session, supports dry-run by default,
and should be run in bounded batches with limit and cursor. It compares
historical wrong answers against current CloudBase grading_keys and applies
the same upward-only attempt, assignment, and STAR repair rules.
Teacher-approved CloudBase grading corrections are authoritative. Future
content imports must not blindly overwrite revised grading_keys; reconcile
their grading_version and grading_key_history first.
Student Argue replies have two student-facing surfaces:
- the original question's Argue dialog is the permanent status/reply view
- the To Do List modal's top-right speech-bubble
Teacher Repliescontrol is a permanent inbox of all resolved replies
When a teacher resolves a student Argue request, set student_seen: false and
student_seen_at: null. The speech-bubble badge counts only unseen replies.
Opening and closing the replies inbox calls
getDashboard.markTeacherRepliesSeen, which clears that badge without removing
the replies from history. Teacher replies must not be mixed into assignment
sections; they open from the bubble in the To Do List header. Historical answer
views may show Argue status and teacher notes. When a
student reopens completed work and clicks History, each historically wrong
question must still render the student Argue entry point against that historical
attempt_id; if the assignment has already recorded answer_revealed: true,
history may also show a collapsed per-question Explain action.
New student Argue requests also create recoverable private email intents. The
existing teacher-email timer sends one immediate student_argue event per
request. The email opens argue-review.html?dispute=<id>; the locator never
bypasses active teacher authentication. The card supports three ordinary
choices, optional Teachers’ Note and explicit Submit; Listening retains its
keep/provide choices. Both entry points use the same transactional decision
and grading-history boundary. Retry an unfinished decision without changing
its saved choice or creating a second grading version. Do not parse ordinary
email replies as decisions or backfill emails for historical disputes.
Vocabulary
Only Vocabulary Quiz mode can create a countable progress attempt. The
current Quiz selector offers 5 or more groups; those Quiz submissions are
countable and stored. Timed Cloze Practice also stores an activity attempt as
mode: "vocabulary_practice_timed", regardless of whether the student selects
1-4, 5, or more groups, solely so it can appear in teacher notifications and
paper review. Practice activity attempts remain excluded from assignment
progress, FINISHED, the student calendar, STAR, learning reports, student
progress, and future assignment initialization. Learn inline practice remains
unrecorded.
Countable vocabulary attempts retain selected_group_count,
selected_group_ids, overall score, and per-group group_results. Groups are
random, so these details are required history.
9. Public and Private Content
Public repository/runtime data may contain passages, transcripts, questions, choices, IDs, and display metadata.
Premium reference artifacts are different from ordinary public lesson content.
If visitors receive only a blurred or partial preview, the complete artifact
must not be committed to the public repository or embedded behind CSS blur.
Keep the full source outside Git, generate the ignored
cloudfunctions/getProtectedResource/protected-payloads.private.js, and place
it only inside the ignored CloudBase deployment ZIP. The cloud function must
derive identity from authenticated context and require an active student or
teacher profile before returning any protected chunk.
Private CloudBase grading_keys contains correct answers, accepted variants,
explanations/evidence, and scoring rules. Do not add new answers to public
runtime JSON. Do not commit generated private import files.
Run:
node scripts/prepare-cloudbase-data.js
It creates ignored output under .cloudbase-private/:
import/sets-cloudbase.json: JSON Lines forsetsimport/grading-keys-cloudbase.json: private JSON Lines forgrading_keysimport/system-config-cloudbase.json: JSON Lines forsystem_configpublic/: answer-stripped runtime previews
Preferred owner-run CloudBase data import is CLI:
npm run cloudbase:import:content
npm run cloudbase:import:content -- --apply
The first command is a dry run. The second writes missing sets and
grading_keys records to the development CloudBase environment. It does not
overwrite existing records by default, protecting teacher-approved grading
changes made through Argue. CloudBase console import of the -cloudbase.json
JSON Lines files is now only a fallback. Never commit .cloudbase-private/.
10. Turning Owner Material Into Website Content
When the owner supplies Markdown, PDFs, raw questions, corrections, or a new content idea, do not ask them to create JSON or wire links manually.
Default workflow:
- Identify the family: BBC, IELTS Reading/Listening, Vocabulary, another permanent section, or temporary classroom material.
- Read the matching template in
content/templates/and existing examples. - Choose or preserve a stable ID:
- BBC:
BBC-YYMMDD - IELTS:
C7-T1-P2style - Vocabulary:
NGSL-Astyle
- BBC:
- Preserve a human-readable source layer when one exists, especially BBC Markdown maintained by the owner.
- Create/update canonical metadata and content under
content/. - Create/update runtime exercise data under
data/using the shared schema. - Keep grading data private in the deployed output.
- Run the relevant importer and
node scripts/build-home-catalog.js. - Run
node scripts/prepare-cloudbase-data.jswhen sets or grading change. If CloudBase data must be imported, give the owner terminal commands, normallynpm run cloudbase:import:contentandnpm run cloudbase:import:content -- --apply, instead of asking them to use the CloudBase console. - Validate IDs, counts, links, grading-key coverage, and interaction.
- Tell the owner exactly which CloudBase import/deployment is required.
For corrections, update structured content, private grading data, and the owner-facing Markdown source when one exists. Never fix only visible text while leaving grading inconsistent.
Content edition rules
- Create V2/V3 only when the owner explicitly identifies the material as a new edition; otherwise treat supplied changes as an update to the current set.
- Never rename an existing
set_idthat may already be referenced by assignments, attempts, STARs, or disputes. Keep the original ID for V1 and suffix only new editions, for exampleBBC-250904-V2. - Relate editions with explicit
edition_family,edition_number,edition_label, andis_latest_editionmetadata in both canonical content metadata and CloudBasesets. Do not infer relationships from similar titles. - Editions remain independent for Assign, attempts, thresholds, progress, and
STARs. Student Library may group them for discovery, but record-owned links
always use the concrete
set_id. - Current policy keeps every edition visible to students and assignable by teachers. Do not add archive or new/old-student visibility rules unless the owner explicitly requests them.
- Same-edition corrections may retain
question_id. Never downgrade immutable history; accepted-answer repairs may only regrade upward.
Temporary classroom HTML may remain independent until it becomes recurring.
BBC review and import gotchas
For BBC listening lessons where the owner supplies only a transcript, generate
a teacher review draft before touching website data. Keep the review draft in
/private/tmp or another non-repository location because it contains answers.
The teacher review draft should show each question with its answer and a
slightly longer evidence quote, using transcript line numbers only, such as
L23 or L23-L25; do not use paragraph labels like P03-L01. Do not include
a separate Why field in the teacher review version; fold the justification
into the evidence quote.
Only after the owner approves the teacher review draft should the agent create
or update project data. BBC fill-in-the-blank placeholders in data/BBC-*.json
must use exactly five underscores:
_____
bbc.html currently replaces only the first _____ token with an input. If a
sentence uses ________ or any longer underline, the extra underscores remain
visible after the input field. Always scan new BBC data for _{6,} before
committing.
When a blank has multiple accepted answers, store answer as an array in the
canonical data/BBC-*.json; submitAttempt accepts array answers and checks
them with normalized exact matching. This is the correct way to support variants
such as British/American spelling.
BBC lessons may opt into alternate front-end presentation with renderTheme
in data/BBC-*.json and matching content metadata. Treat this as display-only:
do not change question IDs, grading keys, attempts, History/Clear, Explain, or
Argue behavior when assigning a theme.
IELTS Reading PDF import gotchas
IELTS Reading content may arrive only as PDFs. The current static reading page
loads runtime data from data/<set_id>.json, while homepage/library metadata
lives in content/ielts-reading/<set_id>.json. After adding or changing
metadata, run:
node scripts/build-home-catalog.js
Do not hand-edit data/home-catalog.json or data/home-catalog.js; they are
generated from content/.
Before inventing a new IELTS question shape, inspect ielts-reading.html.
During the Cambridge IELTS 7 import, the page supported these runtime types:
tfngynngmcqsummaryheadings
Some original IELTS formats were represented through existing controls:
- Paragraph matching can be entered as
summarywith A/B/C choices. - Classification can be entered as
summarywith choice labels. - Map/route matching can be entered as
summarywith route-letter choices.
This keeps the page reusable, but it also means the data label/instruction must be clear for students. If the UX needs a new interaction, add it deliberately to the shared page and update this guide.
PDF extraction has a few recurring traps:
pdftotextmay not be installed on the machine. Check available tools before assuming it exists.- Python
pypdfmay be available and can extract many Cambridge PDFs well. - File names can contain apostrophes, such as
Let's go batsordon't fall down; avoid fragile shell quoting when passing those paths. - Some PDFs have incomplete text layers. In the C7-T2-P3 Makete import, the last page extracted only part of the questions. Use OCR or a verified external copy for the missing part, and explicitly tell the owner if any question, answer, or evidence remains uncertain.
- Validate each new
data/C*.jsonandcontent/ielts-reading/C*.jsonwithJSON.parse, then count expected questions before committing.
Watch for schema drift around answers. Older/static IELTS runtime data may
include answer and evidence because ielts-reading.html grades locally.
New CloudBase-backed counting work should keep grading material private through
grading_keys and prepare-cloudbase-data.js. Check the current target flow
before adding a new set so public runtime, private grading, and teacher feedback
do not diverge.
IELTS Listening PDF/audio import gotchas
IELTS Listening uses the shared runtime page ielts-listening.html. Do not
create a permanent standalone page for each section or test.
For a new listening set:
- Put homepage/library metadata in
content/ielts-listening/<set_id>.json. - Put public runtime question data in
data/<set_id>.json. - Put audio under
assets/audio/ielts-listening/and reference it from the runtime JSON. - Use
{{Q1}},{{Q2}}, etc. placeholders in public question HTML; the runtime replaces them with answer inputs. - Do not put answers, accepted variants, explanations, or answer-key screenshots in public runtime JSON.
- Put private answer material in ignored local source files such as
.cloudbase-private/source/ielts-listening/<set_id>.json;scripts/prepare-cloudbase-data.jsreads these files to creategrading_keys.
Cambridge Listening materials may arrive as separate question PDFs, tapescript
PDFs, answer-key images, and audio files. pdftotext may be unavailable;
Python pypdf can often extract the question sheets and tapescripts well enough
for Section 1-style completion tasks. Always validate question IDs, accepted
variants such as British/American spelling, and the generated private grading
key count before importing to CloudBase.
11. Cloud Functions and Deployment
Active backend functions:
getCurrentStudent: authenticated profile lookupgetDashboard: assignments and student summarygetResources: visible practice setsgetProtectedResource: authenticated delivery of private student/teacher reference artifactssubmitAttempt: grading, attempt storage, assignment updatestudentVocabulary: authenticated student personal word listteacherAdmin: teacher-only account, assignment, and data actions
Source lives in cloudfunctions/<name>/; deployment ZIPs live in
deploy-packages/<name>.zip. Edit source first, test, then rebuild the ZIP.
Never hand-edit a ZIP.
teacherAdmin configuration:
- Node.js 18
- 256 MB
- initialization timeout 65 seconds
- execution timeout 10 seconds
- automatic dependency installation
INITIAL_STUDENT_PASSWORDenvironment variable
Deploy only to the development environment unless the owner explicitly
approves production. Follow docs/10_DEPLOYMENT.md.
12. Frontend and Visitor Rules
Student dashboard navigation:
- The lower main navigation exposes only
Library. - Assignments and finished work live in the standalone far-left
To Do Listchecklist button, visually separated from the right-side utility controls. - The Student Dashboard header does not render a cat logo; the far-left To Do List button stands alone so the header keeps maximum usable space. This does not change the Teacher header's existing brand mark.
- The default
To Do Listmodal has no top summary capsules. Its first open-work section is labelledThis Weekand is an open-by-default disclosure; the finalFinishedsection is a closed disclosure by default. Neither header shows a numeric count pill, and Finished tasks sort by newest completion first. TheASSIGNMENTStitle uses the same small green accent typography
Truncated - read the full file at https://github.com/jxbleo/jxbleo.github.io/blob/5ee5b294c3a524f46167365d5b2dec4459912be7/AGENTS.md.
