๐Ÿ“š
Mr. Wiessmann

System Guide

Academy at Palumbo
๐Ÿค–

Handing this off to an AI?

Give it SYSTEM_REFERENCE.md instead of re-explaining the system - it's written specifically so a fresh AI session can understand the whole architecture with zero prior context.

Download SYSTEM_REFERENCE.md
Architecture

Two Visual Themes

This site deliberately uses two different looks for two different kinds of pages. Don't mix them when building something new.

DARK / TERMINAL

Near-black background, JetBrains Mono, amber + teal accents. Where students actually do the day's work.

day.html · test.html

Light / Friendly

Soft lavender background, Fredoka headings, warm orange accent, white rounded cards. Landing/reference/utility pages.

index.html · policies.html · ask-a-question.html · this page
Complete File List

Every File On This Site

Direct links to everything - the actual live files, not a description of them.

Root pages
js/
css/
labs/ (separate system - see ยง9 of SYSTEM_REFERENCE.md)
Deployment

Installing the Scripts

Everything here is plain HTML/CSS/JS - no build step, no dependencies to install. This is the order that actually works, start to finish.

  1. Upload the site files, preserving the folder structure exactly - js/, css/, and labs/ (with its own js//css/) all need to sit where the HTML expects them.
  2. Create the Google Sheet(s). One public spreadsheet for pacing/vocab/quiz/resources tabs, and one separate, never-published private spreadsheet for roster, help_queue, star_log, star_totals, and student_work.
  3. Publish each public tab to web individually - File → Share → Publish to web → pick the specific tab (never "Entire Document") → format Tab-separated values (.tsv) → copy the resulting URL.
  4. Paste each published URL into its matching constant at the top of index.html / day.html (see the comment above each URL constant for which tab goes where).
  5. Deploy Code.gs from the private spreadsheet: Extensions → Apps Script → paste in Code.gs → Deploy → New deployment → Web app → execute as yourself, accessible to "Anyone" → copy the deployment URL.
  6. Paste that one deployment URL into STAR_API_URL in js/student-id.js and API_URL in ask-a-question.html - same URL in both places, one backend for the whole site.
  7. Lab tools reuse that same URL. Each lab's own script calls PCSCertificate.render({ webAppUrl: ... }) with that identical deployment URL - don't stand up a second Apps Script deployment just for labs.
  8. Test before going live with test.html (hardcoded example data, no spreadsheet needed) or a real day = -1 row in the daily content tab (reserved for test data).
Publish tabs individually, never "Entire Document" - and never publish roster, help_queue, star_log, star_totals, or student_work at all. Those four (five, with student_work) hold live per-student data and are only ever read/written by Code.gs.
Full step-by-step detail, including the exact column headers every tab needs, lives in SETUP.md.
Architecture

How day.html Actually Builds a Page

day.html has almost no fixed content - it's a thin shell that day-loader.js fills in dynamically at load time, entirely from spreadsheet data.

  1. Reads ?course= and ?day= from the URL.
  2. Shows the full-screen loading overlay immediately.
  3. Fetches the pacing tab, the daily content tab, and (if needed) the terminal-steps tab, in parallel.
  4. Finds the one pacing row matching both the course and the day - not day alone, since the pacing tab now covers all 3 courses.
  5. Builds the hero: title, small Learning Objective / Essential Knowledge reference lines, day/date pills.
  6. Builds every widget for that day in the exact order the rows appear in the spreadsheet. Reorder widgets by reordering rows - there's no separate ordering column.
  7. Builds the certificate, tracking every trackable widget on the page.
  8. Fades out the loading overlay and draws in the four corner-flourish decorations.
The one exception to row order: an instruction row with field_d set to exactly after_certificate renders in its own separate slot below the certificate, regardless of where the row sits in the sheet. That's the mechanism for "here's exactly what to do with the rest of class" - the literal last thing a student sees on the page.
The same widget type can appear more than once on the same day, even non-adjacent to other rows. Each instance gets its own container and its own certificate entry, correctly numbered (1)/(2) only when more than one exists that day.
If a pacing row exists but there are zero daily-content rows for that day, the page shows "Content Coming Soon" instead of a blank page. If no pacing row exists at all, it shows "No day scheduled here yet."
Content Authoring

Widget Reference (16 types)

Every widget is one row in the daily content tab: course | day | widgetname | field_a | field_b | field_c | field_d | notes. The same 4 generic fields mean different things depending on widgetname. Click any widget to expand its exact field usage.

Universal conventions: wherever there's a "correct answer," it's always listed first in the sheet - the widget shuffles the display order itself. ;; separates list items; :: separates sub-fields within one item.
Spreadsheet Reference

Every Spreadsheet Tab

Public tabs are published to web (File โ†’ Share โ†’ Publish to web โ†’ choose the specific tab, format TSV) and read by the website. Private tabs are read only by the backend Apps Script and should never be published.

TabTierRead byColumnsNotes
pacing timelinepublicindex.html, day.html course, day, date, weekday, term, unit, topic_activity, day_type, learning_objective, essential_knowledge, mapping_note, possible_lab_project (+Cyber-only: scenario, hands_on_activity, hands_on_note) Merged - all 3 courses, one tab. course = "ap csa"/"ap csp"/"ap cyber" (case/spacing-insensitive).
daily contentpublicday.html course, day, widgetname, field_a, field_b, field_c, field_d, notes The master content tab. day = -1 reserved for test data.
vocabularypublicindex.html type, course, category, term, description, example Merged with Cheat Sheets. type = "vocab" or "cheat...". Blank course matches nothing - list every applicable course explicitly, semicolon-separated.
frqspublicindex.html course, unit, question, excellent student response, weak student response Header must say exactly course.
quiz questionspublicindex.html course, unit, question, option_a...option_h Standalone MCQ tab - distinct from the daily-content quiz widget. option_a always correct; 3 of the remaining up-to-7 wrong options chosen at random each attempt.
resourcespublicindex.html name/description/url
terminal-stepspublicday.html step_id, label, command, arg, require_arg_match, require_cwd, notes, fs_template ~77 steps, 8 filesystem templates. Never set require_cwd on a cd step.
classroom rulespublicday.html, policies.html One column: rule Read live by both the per-day rule flag-row widget and policies.html.
rank_titlesprivateCode.gs only course, title, stars_to_get_title Not a published TSV - read via the Sheets API. course uses the plain key with no spaces (apcsa/apcsp/apcyber), a different convention from the pacing tab, intentionally.
bulletquestionsprivateCode.gs only course, question Powers the Quick Logistics buttons. Known bug: a stray "ap apcyber" typo currently breaks 2 rows for Cybersecurity.
help_queue, roster, star_log, star_totalsprivateCode.gs only Live operational student data. Never publish these to web.
Student-Facing (Private)

ask-a-question.html

A fully functional help-queue page backed by Code.gs (deployed separately as a Google Apps Script Web App) - not a spreadsheet-TSV page. Everything goes through the Apps Script's own API.

  • Quick Logistics - instant, no queue, no credit; pulled from bulletquestions.
  • Need Help mode - joins the visible queue (name shown, not the question).
  • Verbal Question mode - logs a question already asked out loud, for credit, doesn't join the queue.
  • Auto-fills the student's name from localStorage if they've already confirmed their ID elsewhere on the site.
Status

Pending Items & Known Limitations

Still needs setup: GENERAL_MESSAGES_TSV_URL in day.html is a placeholder - the messages tab (date/message columns) doesn't exist yet.
Live data bug: "ap apcyber" typo in bulletquestions breaks 2 Cybersecurity quick-questions.
Content coverage: real widget content exists for days 1-5 only, across all 3 courses. The full-year pacing guides exist, but days 6+ have no daily-content rows yet - ongoing work, not a bug.
Known, accepted limitation: the stepper, parsons, and debug-challenge widgets each support one language per spreadsheet row - the multi-language switcher only exists in hand-authored JS configs.
By design, not a gap: this system has no open-ended "write real code and see it render live" widget - every widget simulates a fixed, narrow slice of behavior. For genuinely open-ended building, linking out to an external tool (Codio, Replit, etc.) alongside the short "lecture" page is the intended pattern.