Skip to contents

Why things are the way they are, for the ones that are not obvious from the code and that someone would otherwise undo by accident.

Append-only. Add new entries at the bottom with the date you made the call. Do not rewrite or delete an entry when a decision is superseded — add a new one that says so and link back. These are deliberately not numbered ADRs: this is a one- to-two person team and the ceremony costs more than it returns.

Each entry says what was decided, why, and what it costs. The cost line is the important one — it is what tells the next person whether the tradeoff still holds.

Open work lives in OPEN_WORK.md; live deploy state lives in ARCHITECTURE.md. This file is only for reasoning.


2026-08-04 — Site and subsite choices are additive only

Decision. update_site_choices.qmd never deletes or renames a Qualtrics choice. Sites retired from the sheet are left in the survey; qualtrics_checks.qmd reports them as survey-only entries for a human to retire deliberately.

Why. A site’s choice id is a foreign key in four places at once: display logic locators (q://<site_qid>/SelectableChoice/<id>) on the course question and both Participant Feedback facilitator questions, the PF survey flow written by update_pf_flow.qmd, the subsite cascade inside each survey’s local site block, and every historical response already recorded against that choice. Deleting a choice silently breaks all four — nothing errors, the logic just stops matching.

Cost. The choice lists only grow, so the dropdowns carry retired partners until someone prunes them by hand. There is no clean undo for a bad push: once a choice exists it is effectively permanent. Everything else about the gate design below follows from this one constraint.


2026-08-04 — The Official Site List tab is the gate for driving subsites

Decision. A subsite column on the FY27 School/District Selection for Sites tab is only pushed to Qualtrics if its site also appears on the FY27 Official Site List tab.

Why. The subsite tab is hand-maintained and accumulates scratch columns for sites that were never onboarded, holding placeholder school names — SL_EPLA carried “Ava High School” and “Booster High School”; SL_WI Victory, SL_NYC D4 and SL_NYC D8 are the same shape of leftover. Combined with additive-only, a single bad column would put fake schools into six live surveys permanently. The site list is the one tab that gets deliberate attention, so it is the gate.

Cost. Adding a row to the Official Site List is now a live survey change, not a bookkeeping edit — it can create questions in six surveys on the next run. That power sits in a spreadsheet with no review step. Two sites are also blocked harmlessly as collateral: DC_DCPS Cluster 8/9 and TN_Acceleration for All Region/District, which the ambiguity filter already excluded under their old names.


2026-08-06 — The site → question mapping is derived, not declared

Decision. Which subsite question belongs to which site is read back out of each question’s display logic (q://<site_qid>/SelectableChoice/<id>), not looked up in a maintained list.

Why. The maintained list was tlShiny::subsites, and it had drifted measurably. Checked against the live Educator Survey it agreed on only 35 of 38 mappings: it said MA_Boston and NY_CPS_OpenSciEd where both the surveys and the sheet say MA_Boston Prep and NY_NYCPS_OpenSciEd, and it mapped d13_prek_subsite to NY_D13 Pre-K where the question is really conditioned on NY_D13. A declared list is a second copy of something the survey already knows, and the second copy rots.

Deriving also made three former hardcoded exceptions fall out for free: the text-entry NYC districts register as covered (so they are never handed a choice list), a site resolving to two questions (DC_DCPS → clusters and schools) is reported and skipped, and the tn_acceleration2 cascade hangs off the region question so it simply never claims a site.

Cost. The mapping is now only knowable by calling the Qualtrics API — you cannot read it out of the repo. A question with missing or hand-edited display logic silently drops out of the mapping instead of erroring. tlShiny::subsites is deprecated and survives only as a warned fallback in tracker_27.qmd.


2026-08-06 — The subsite tag list is published as a registry object

Decision. update_site_choices.qmd writes the derived mapping to tl-surveys-27/subsite_question_registry_fy27.parquet (survey_id, site, question_tag, question_type, qid). That object, not tlShiny::subsites, is the downstream contract for “which export column holds the subsite for which site”.

Why. Deriving the mapping fixed the drift but left it trapped inside one render log. Publishing it is what makes a newly created question visible downstream with no hand edit — which was the whole point of automating question creation.

Cost. A new cross-repo dependency on an S3 object, and a consumer (tracker_27.qmd) that has to carry fallback code for when it is unreadable. The registry is only as fresh as the last successful run of the job that writes it.


2026-08-06 — FY27 has its own Master Survey Input sheet

Decision. FY27 reads from 1hbs5d1uf2xq… (“FY27 Master Survey Input for Automation”). FY26-and-earlier readers stay on the original sheet (11jlo9UeWxZ…).

Why. L&R split FY27 onto a new sheet in August 2026. The original sheet kept its FY27 tabs, and those copies have since diverged — reading FY27 from there both misses new sites and makes qualtrics_checks.qmd report false differences.

Cost. Two sheets that must both stay alive, and a rule with no enforcement: FY27 must never be read from the old sheet, and FY26 must never be read from the new one, because the new sheet has no FY26 tabs and those readers would hard- error if moved. Seven files are still on FY26 tabs for exactly this reason and cannot migrate until FY27 tabs exist — tracked in OPEN_WORK.md.


2026-08-06 — PF facilitator handling is its own job

Decision. The Participant Feedback facilitator half was split out of update_coach_selection.qmd into update_facilitator_choices.qmd.

Why. They are different shapes of problem despite looking like duplicates. The coach job maintains a choice list — a roster of who exists — from the Monday facilitator board plus full-time employees. The facilitator job writes per-site display logic onto PF’s QID224/QID109, sourced from an S3 pl_today_facilitators_fy27.rds feed refreshed daily by a separate Connect job, so that a participant only sees the people who actually ran their session.

Cost. Two similarly-named jobs that are genuinely easy to confuse, made worse by the same person often being both a coach and a facilitator, and by PF’s second facilitator question carrying the export tag coach_2 while being maintained by the facilitator job. The original file also kept a stale header claiming it still handled PF, which stayed wrong until 2026-08-07. Both files now carry an explicit cross-reference.


2026-08-07 — Target questions are resolved by DataExportTag, not hardcoded QID

Decision. update_coach_selection.qmd resolves its target question by DataExportTag == "coach" in each survey rather than naming QID469/QID39/ QID3 as literals.

Why. Hardcoded QIDs are what let this job’s FY27 migration get missed entirely. It sat live on Connect maintaining coach lists on the FY26 IPG and Student Work surveys while the FY27 copies got no updates at all, and nothing failed — the FY26 QIDs still resolved, so every run looked healthy. A survey id that is wrong should break loudly; with a tag lookup and a stop() on no match, it does. The export tag is stable across fiscal years in a way the QID is not guaranteed to be, even where FY27 happens to reuse the same numbers.

Cost. One extra QSF fetch per survey, and a hard dependency on the export tag being spelled identically across surveys — renaming a tag in Qualtrics now breaks the job rather than silently retargeting it. That is the intended failure mode, but it is a failure mode.


2026-08-08 — The five sites dropped from the FY27 site list are real exits

Decision. ar_osceola, ct_hamden, ma_westspring, tx_elpaso and tx_la_joya fell off the FY27 Official Site List in the sheet move, and that is correct: partner work with all five is finished. They are no longer driven and stop receiving new subsites. Deprecating update_la_joya_pf.qmd on the strength of that drop was the right call, and it stays in posit_automations/deprecated/.

Why. The drop was found by diffing the FY27 Official Site List against the previously driven sites (2026-08-06, commit 9ed7cccc) and was carried as open work because nobody had confirmed it was intentional rather than an oversight in the two-sheet split — see FY27 has its own Master Survey Input sheet. Confirmed 2026-08-08.

Cost. Nothing was removed from Qualtrics, because the choice jobs are additive only — see Site and subsite choices are additive only. All five remain as selectable choices on the FY27 surveys and will keep appearing in dropdowns until someone prunes them by hand, and qualtrics_checks.qmd will keep reporting them as survey-only entries. That report noise is the intended signal, not a bug.

TX_La Joya ISD also stays in the excluded_sites guard at api/monday-webhook/plumber.R:974 and :1163. Removing it there would switch coaching-log processing back on for the site, so the dead entry is the safe state and should be left alone.