# PRD — Click Foundation - Migration

Evidence date: 2026-10-19. Source: `git@bitbucket.org:overdrive/click-foundation-migration.git`, `master` at `fd0c90b` (2022-12-09, "Adding lambdas and changes to lambdas", Dirk). Step Function JSON was also read from `origin/prod` at `f47d0c3`, because those files are not on `master`.

Provenance labels: **Documented**, **Implemented**, **Inferred**, **Planned**, **Unknown**.

No formal PRD existed in the repository or in `silicon_ark/Click Foundation - Migration/` before this file. Requirements below are recovered from the implementation and from the operational documents stored in the same repository. They are not a new product decision.

The planning workbook names the effort **The Click Foundation AWS Migration and Modernization**. The product being migrated is The Click Foundation's system for schools, learners, and intervention programmes (Reading Eggs, Matific, Reflective Learning, and quizzes). This repository is the migration workspace, not the learner-facing portal.

## Users and roles

No application login screen exists in this repository. People appear in documents and in SQL that serves portals hosted elsewhere.

| Role | What the evidence says | Provenance |
|---|---|---|
| Click Foundation operations | Supply launch lists, CEMIS files, and facilitator changes. Named participants in the planning workbook include Willem Boshoff (`willem@clicklearning.org`). The May 2022 transcripts include Nicola and Willem as customer voices. | **Documented** |
| Meraki / implementation operators | Run imports, check the DDD file-check workbook, and execute registration. The maintenance note says Meraki completes facilitator changes requested by Kirsty. Email addresses for status mail sit in `DDD and CEMIS Import.docx`. | **Documented** |
| School facilitators | Maintained by a manual spreadsheet process (add, suspend, update). A later plan says passwords should move to Google, and that this was outside the statement of work. | **Documented** process. Google sign-in is **Planned** in the workbook, not implemented here. |
| Learners | Have programme usernames. Reading Eggs usernames come from Reading Eggs. Matific-only and Reflective Learning usernames are generated in `ClickDW`. Learners sign in on `https://learn.clicklearning.org` according to the eQuiz note. That site is not in this repository. | **Documented** URL. Username generation for programmes 8 and 9 is **Implemented**. |
| Principals, RSAs, and other report recipients | Usage reports go to people flagged "GET REPORT". Registration files go to Reading Eggs (Carol in the rules) and to RSAs. | **Documented** |
| Silicon Overdrive engineers | Own the AWS stacks, Lambdas, and converted SQL. Budget notifications in the v5 budget template go to Heinrich, `awsbudgetalerts@overdrive.co.za`, and `AWS.Escalations@overdrive.co.za`. | **Implemented** addresses in the budget template |
| Portal identity user | `functions/Portals` defines `portal.pidentitybyusername`, `portal.puserbyusername`, `portal.puserbyuserid`, `portal.puserredirectto`, and a school-list filter. Who may call them is decided by applications that are not in this repo. | **Implemented** SQL. Callers **Unknown** from this repo alone. **Inferred** match to ClickPortal / ClickLearnerPortal. |

There is no role or permission model in the Lambda code. A Lambda that can read Secrets Manager and reach the VPC can run the SQL it contains.

## Major workflows

### DDD learner import (provinces other than Western Cape)

Documented sequence (`DDD and CEMIS Import.docx`, `SSIS and Procedures progress.txt`, `System Business Rules.xlsx`):

1. DDD supplies three files (main extract, changed ids, merged ids), described as Sunday evening / Monday morning.
2. A Monday 08:00 job fetches them from FTPS if that day's file is not already imported.
3. `Import_Learner_DDD` loads staging tables `learner`, `learnerdddchangedid`, and `learnerdddmergedid`, then writes a DDD file-check workbook.
4. A person reviews that workbook. If it looks consistent, `CompleteDDDFileImport` runs. The business-rules sheet says: if the script shows a result, stop; if it shows nothing, continue.
5. `Import_learner_DDD_Finalize` updates source learners and first-usable data.
6. `LearnerRegistrationCheck` is a reasonability check on how many registrations will occur.
7. `TransferDW_Learner_Registrations` registers learners and generates usernames.

Implemented Lambda path on `master` (functions exist; they are not wired to a schedule in this tree):

1. `ddd-get-files` lists an S3 bucket from `event.detail.bucket.name`. It continues only when it counts 3 keys that share the date taken from the first key. It expects names `Click Foundation Extract {date}.csv`, `Click Foundation Exception Report Merged Ids {date}.csv`, and `Click Foundation Exception Report Changed Ids {date}.csv`.
2. `ddd-get-dataimportid`, `ddd-import-files` (database `ClickSTAGING`, secret `staging-aurora`), `ddd-update-dataimport`.
3. `ddd-complete-ddd-file-import` calls `internal.pcompletedddfileimport()` on `ClickDW` (secret `dw-aurora`).
4. `ddd-import-learner-ddd-finalize`, `learner-registration-check`, `ddd-move-files`.

On `origin/prod`, `Step Functions/Import_Learner_DDD.json` runs those steps, then in parallel runs complete/finalise/registration-check, `cemis-launch-reports`, and `ddd-move-files`, then `ddd-vacuum-learnerdatareadingeggs` and `send-email`. The vacuum and send-email functions are not folders on `master`. The state machine comment still says "CEMIS - Import_Learner_CEMIS_Simple".

Provenance: sequence **Documented** and **Implemented** as separate Lambdas. Orchestration **Implemented** only on `origin/prod`. The Monday 08:00 schedule is **Documented** for the old SQL Agent job and is **not** an event on the Lambda `serverless.yml` files.

### CEMIS learner import (Western Cape)

Documented (`CEMIS Learner Import.docx`, dated change history 5 and 14 February 2020, author Amanda; process notes; business-rules sheet):

1. Western Cape schools do not use DDD. Facilitators download CEMIS and staff consolidate files (Metro and Winelands are named).
2. Files are moved to a processing folder so Click stops editing them, the top row is removed, and each file is saved as CSV.
3. `Import_learner_cemis_simple` validates. Failure writes an error file and stops. Success updates learners, source learners, first-usable data, and school data source.
4. The same registration check and transfer as DDD follows. DDD and CEMIS may run in either order.

Implemented Lambdas: `cemis-get-files`, `cemis-get-dataimportid`, `cemis-import-files`, `cemis-validate-learner` (`reporting.pvalidatecemislearner`), `cemis-update-datejoined-dateleft`, `update-dataimport` (`CALL dbo.pupdatedataimport(..., 'Success', 'Completed')`), `cemis-update-source-learner`, `cemis-update-first-usable-data`, `cemis-update-shool-data-source` (folder name spells "shool"), `cemis-launch-reports` (Python 3.9), `cemis-move-files`.

On `origin/prod`, `Import_Learner_CEMIS_Simple.json` chains those steps and then runs registration-check, launch-reports, and move-files in parallel, then `send-email`.

`cemis-import-files/index.js` contains `UPDATE t$cemis_import SET DataDateYear = '2020'` with a comment to use a parameter instead. Provenance: **Implemented** literal. Whether a caller overwrites it is **Unknown**.

### Learner programme registration

Documented: after a successful import and a reasonability check, the system creates Reading Eggs files per region (learners and teachers), a Matific file, and a Reflective Learning file, and emails them. Reading Eggs registration was still a file to Carol / RSAs. API registration is written as a future step.

Implemented on `master`:

- `transferdw-learner-registration-get-learner-count` calls `dbo.plearnersregisterfromsourcelearnerf` on `ClickSTAGING`.
- `transferdw-learner-registration-update-learners` on `ClickDW` calls `public.fupdatedone`, `internal.pnewdataimportid`, `internal.pTransferLearner`, `internal.pupdatelearnerfromsourcelearner`, `internal.pUpdateDataImport`, and `internal.pgenerateusernames`.
- `transferdw-learner-registration-export` builds Reading Eggs extracts with `internal.pgetprogrammeregistrationregionlistf`, `internal.pExtractProgrammeRegistrationRegionLearnerListReadingEggsf`, and `internal.pExtractProgrammeRegistrationRegionClassListReadingEggsf`, and a Reflective Learning extract with `internal.pExtractReflectiveLearningRegistrationLearnerListf('South Africa')`. Failures are logged as "copy failed".
- `transferdw-learner-registration/index.js` contains the same SQL in one function.

On `origin/prod`, `TransferDW_Learner_Registration.json` runs get-count, update-learners, export, `send-email`, `transferdw-learner-registration-export-refl`, and `send-email` again. `export-refl` is not a folder on `master`.

### Reading Eggs usage

Documented: a file arrives by SFTP; a job polls from Sunday afternoon until 02:00 Monday; usage is imported; reports and an exception report are produced. Matific and Reflective Learning usage were explicitly deferred in the 10 May 2022 meeting.

Implemented: `import-readingeggs` and `import-readingeggs-learnerlist` call `dbo.pnewdataimportid`, `aws_s3.table_import_from_s3`, and `dbo.pUpdateDataImport` on `ClickSTAGING`. Transfer Lambdas on `ClickDW` call `internal.pNewDataImportID`, `internal.pUpdateLearnerFromReadingEggsf`, `internal.pTransferReadingEggs2`, `internal.pUpdatePersonProgrammeActivity('4')`, `internal.pactivateschoolreadingeggsf`, and `internal.ptransferreadingeggsnewlearnerlist`. A comment in `transferdw-readingeggs/index.js` says `pactivateschoolreadingeggsf` has a "CONVERSION ERROR".

On `origin/prod`, `Import_Readingeggs.json` loops import → start → choice → update learner → transfer → person-programme activity → activate school → send email → start again. `Import_Readingeggs_Learnerlist.json` runs import, transfer learner list, `transferdw-readingeggs-learnerlist-export-matific` (not on `master`), and send email.

The Sunday polling schedule is **Documented** and is not an event in the Lambda YAML.

### School list and environment (programme / grade) activation

Documented order in `Click Import Process and Dependencies.docx`:

1. Create the school (masterlist) before any environment.
2. Masterlist is rare (the note says DDD once, September 2020, and a reduced CEMIS language file in October 2021). Import is `Import_Masterlist.dtsx` then `ClickDW.internal.pTransfer` with `@TranAction="R"` (review, rollback) and then `"C"` (commit).
3. Environment import creates school / grade / programme rows the same way: `Import_Environment.dtsx` then `pTransfer` R then C.
4. A school must already exist. Western Cape schools that are missing are built by hand because they are not on DDD.

Implemented Lambdas: `import-environment` (staging) and `import-environment-transfer`. On `origin/prod`, `Activate_School_Grade.json` runs those two functions and stops. There is no masterlist Lambda on `master`.

School status "active when 25% of the grade is active on Reading Eggs", and a proposed change to 10% of any programme, are in the business-rules sheet. The activation procedure on `master` is `internal.pactivateschoolreadingeggs` / `pactivateschoolreadingeggsf`. The 25% figure was not re-derived from the procedure body in this pass. Provenance of the percentage: **Documented**. Provenance inside the procedure: **Requires Confirmation**.

### eQuiz / TAO

Documented in `Click Foundation - eQuiz.docx` for the previous Mobenzi design:

- Quizzes are configured in the external tool. Click stores `QuizInstanceSource`, question mapping, `Quiz`, and activation rows (`QuizInstance`, `QuizInstanceSchoolGrade`).
- A learner who signs in at `https://learn.clicklearning.org` is sent either to a programme login URL or to the quiz URL.
- Completion data is retrieved once a day, so a learner who finishes still returns to the quiz for the rest of that day.
- The documented SQL Agent job is weekdays at 19:00 and loads `QuizSubmission` / `QuizSubmissionResponse`, then `TransferDW_Quiz`.

The planning sheet later says "Quizzes :: Mobenzi closed so no longer required".

Implemented replacement on `master`: `TAO Datastore API/Delivery/tao-get-delivery` and `tao-delivery-import` request an OAuth token from TAO and insert a data source type described as `TAO: Click Foundation eQuiz`, plus data-contract and data-import rows, in `ClickSTAGING` and `ClickDW`. Other folders under `TAO Datastore API/` (`tao-transferquiz`, delivery-result, test metadata, test-taker) are client stubs or partial handlers; several have no `serverless.yml`. Provenance: TAO client **Implemented**. Mobenzi job **Documented** as the old behaviour and marked closed in the workbook. The weekday 19:00 schedule is not in the TAO YAML.

### Facilitator maintenance

Documented only (`Maintenance Items - WIP.docx`). Kirsty sends a Google Sheet with users to add, suspend, and update. Meraki copies rows into `Facilitator Update.xlsx`, which builds SQL, then runs `Facilitator Update.sql`. The note says a front end was envisaged and no automation was built. The planning workbook lists Google-based facilitator sign-in as 40 hours outside the statement of work.

### Calendar-year rollover

Documented only, in `System Business Rules.xlsx` rules RO1–RO7, UR1, LL1, LL2, LM1. No Lambda folder is named for rollover. Whether the rules survive inside `internal.pupdatelearnerfromsourcelearner` is **Unknown** without a line-by-line reading of that procedure (it is long and was converted by SCT). Treat the rollover section in `business-rules.md` as **Documented**, not as proven **Implemented**.

## Functional requirements

### FR-001 — Record every import

The system opens a data-import id, writes status, and can mark the import `Success`.

- Trigger: start of an import Lambda or the old SSIS package.
- Inputs: data-source type, file name or description, data date.
- Behaviour: `dbo.pnewdataimportid` / `internal.pnewdataimportid` insert the id. `dbo.pupdatedataimport` / `internal.pupdatedataimport` set status. `update-dataimport` calls `dbo.pupdatedataimport(id, 'Success', 'Completed')`.
- Output: a data-import id passed to the next step.
- Errors: Lambda `catch` blocks log the error and, in several functions, continue with credentials unset. That is **Implemented** and will fail the database connection rather than return a structured error.
- Provenance: **Implemented**.

### FR-002 — Import three DDD files together

The DDD load runs only when the main extract, the changed-id file, and the merged-id file are all present for the same date.

- Actor: the DDD pipeline.
- Trigger: objects in the S3 bucket named on the event (implemented); previously FTPS (`DDD and CEMIS Import.docx`).
- Validation: `ddd-get-files` counts keys. It proceeds when `fileCount == 3`.
- Output: staging rows and a later file-check report (the report package is named in the progress notes; the package is not in git).
- Provenance: file trio **Documented** and **Implemented**. FTPS fetch **Documented**. An FTP Lambda exists only on `origin/prod` (`get-file-from-ftp-py`) and was not read line by line.

### FR-003 — Apply DDD id changes and merges

Changed-id and merged-id files adjust which DDD learner id is current before the main file is applied.

- Documented checks are in `DDD Changed IDs exception report rules.docx` (2020 and 2021 scenarios). Summary: a changed-id pair should not introduce the new id unless that change is also a merge or was already processed; an id that was an old id in a merged file cannot later be an old id in a changed file; a merge must have both the old id and the shared id.
- Implemented procedure called by Lambda: `internal.pcompletedddfileimport()` and the finalise Lambda. The 2020/2021 exception matrix was not re-proved against the procedure body.
- Provenance: rules **Documented**. Call **Implemented**. Equality of the two **Requires Confirmation**.

### FR-004 — Validate a CEMIS file before it changes the warehouse

A CEMIS file that fails validation is not imported.

- Documented field rules (February 2020 spec): EMIS length 9, or 10 with a leading zero stripped; EMIS starts with `1`; school is ACTIVE or PIPELINE; learner id is 13 characters and starts with a YYMMDD date; age 3–99; gender `MALE` or `FEMALE`; grade `GR` plus `R` or 1–12, with `PRE-GR R` mapped to -1 and `LSEN UNIT CLASS` mapped to 14; blank class allowed; rows that do not start with an EMIS code are ignored; any failing row rejects the file.
- Documented school-level rules: every active grade is present; a grade supplied earlier in the year is present; learner counts per grade stay inside a 10% margin.
- Implemented: `cemis-import-files` deletes rows with empty or longer-than-10 EMIS codes and deletes `GradeID = -1` in one statement. `cemis-validate-learner` calls `reporting.pvalidatecemislearner(dataimportid, 1, 1)`. That function's header comment lists different checks: new learner, possible duplicate (same school, first name, last name, grade), learner left, learner in two schools, movement, and rejoin.
- Provenance: February 2020 spec **Documented**. Procedure comment **Implemented** as comments. The spec and the procedure comment do not describe the same checks. **Requires Confirmation** which set is authoritative. The `GradeID = -1` delete conflicts with the spec rule that `PRE-GR R` becomes grade -1. **Requires Confirmation**.

### FR-005 — Western Cape learners match on CEMIS id

The February 2020 spec says learner matching is on CEMIS id only, the name is split on the comma and converted to proper case, and date of birth is the first six characters of the learner id. A blank class does not create a class.

Provenance: **Documented**. Not re-verified in `dbo.pupdatelearnercemis`.

### FR-006 — Transfer learners into the warehouse and generate programme usernames

After staging is accepted, learners are transferred and usernames are generated for Matific (programme id 8) and Reflective Learning (programme id 9).

- Trigger: registration Lambda, or the documented SSIS package after a human accepts `LearnerRegistrationCheck`.
- Inputs: schools filter (`ALL`, `WC` / `CEMIS`, `NotWC` / `DDD`, or a comma-separated EMIS list) and programmes filter. `WC` means EMIS starting with `1`. `DDD` means EMIS not starting with `1`. **Implemented** in `internal.pgenerateusernames`.
- When programmes is `ALL`, the procedure loads programme ids 4, 8, and 9. It calls `pgenerateusernamesmatific` only for id 8 and `pgenerateusernamesreflectivelearning` only for id 9. It does not generate usernames for id 4.
- Reading Eggs transfer uses programme id `'4'` in `pUpdatePersonProgrammeActivity('4')`. That id 4 is Reading Eggs is **Inferred** from the call site in the Reading Eggs Lambdas.
- Output: warehouse learner rows and usernames where the procedure fills them. Reading Eggs credentials are documented as coming back from Reading Eggs, not from this generator.
- Provenance: **Implemented** call sequence. The human gate before transfer is **Documented** and is not encoded as a check inside the Lambda.

### FR-007 — Export registration files

The system produces Reading Eggs learner and class/teacher extracts by region, and a Reflective Learning learner extract for `'South Africa'`.

Provenance: **Implemented** in `transferdw-learner-registration-export`. Email delivery is a `send-email` Lambda referenced only from `origin/prod` Step Functions. The recipient list is **Documented** (Carol, RSAs) and is not in the `master` export function.

### FR-008 — Import Reading Eggs usage and learner lists from S3

Usage and learner-list files are loaded with `aws_s3.table_import_from_s3` into `ClickSTAGING`, then transferred into `ClickDW`.

- Programme activity update is hardcoded to programme `'4'`.
- School activation runs `internal.pactivateschoolreadingeggsf`. The Lambda comment records a conversion error on that call in `transferdw-readingeggs/index.js`.
- Provenance: **Implemented**.

### FR-009 — Activate a school and grade for a programme from a file

An environment file is loaded to staging and transferred into `ClickDW`.

Documented commit protocol: run `internal.pTransfer` with action `R` to preview and roll back, then `C` to commit. The Lambda `import-environment-transfer` was not fully traced to confirm it still passes `R` then `C`. Provenance of the protocol: **Documented**. Provenance of the Lambda arguments: **Requires Confirmation**.

### FR-010 — Retrieve TAO eQuiz results into the import tables

The TAO delivery Lambdas authenticate to TAO, ensure a data source type `TAO: Click Foundation eQuiz` exists, and insert data-contract and data-import rows.

Provenance: **Implemented** in `tao-get-delivery/index.js` and `tao-delivery-import/index.js`. OAuth client credentials are hardcoded, with the comment "to move out creds". The values are secrets and are not repeated here.

`tao-get-delivery` builds the `ClickDW` connection with the staging secret's password (`secretjson.password`) while the host and user come from `dw-aurora`. Provenance: **Implemented**. Likely defect. **Requires Confirmation** that this was intentional.

### FR-011 — Keep reporting on the existing SSRS stack during phase 1

The 10 May 2022 transcript says reporting, including the PDF reports emailed to schools and the exception report, still runs on Windows EC2 with SSRS. Lambdas trigger it. Email of reports was turned off during testing so that files were written on the server instead.

`cemis-launch-reports` is a Python 3.9 Lambda. `S3-Lamda-SSIS/Python Lambda.py` sends SSM `AWS-RunPowerShellScript` to a hardcoded instance id to run `c:\rootfolder\testbat.bat`. That file looks like a sample (the JSON beside it is `run_my_example.json`).

Provenance: transcript **Documented**. SSM sample **Implemented** as a sample. Production report scheduling **Unknown**.

### FR-012 — Stage and production AWS accounts in Cape Town

The v5 test stacks deploy, in order, VPC, NACL, EC2, RDS, Backup, CloudTrail, Budgets, SNS, and CloudWatch alarms, using AWS profile `tcf-test` in `af-south-1` except budgets, which are `us-east-1`. The prod folder uses profile `tcf-prod` and stage name `prod` for VPC, RDS, EC2, and NACL. Several prod YAML files are still named `tcf-test-*` and the prod CloudTrail, alarms, budget, and backup services in the copy that was opened still used `tcf-test` service names in some files (see technical documentation). That inconsistency is **Implemented** in the tree and **Requires Confirmation** before anyone deploys the prod folder.

Aurora is PostgreSQL 13, encrypted, deletion protection on, two instances (`Primary` and `Read-Replica`), port 5432, not publicly accessible. This contradicts the workbook's Aurora MySQL sketch. The scripts are the later evidence.

### FR-013 — Rollover and pre-valid-date behaviour

Before the new year, active grades for the next year must be loaded (RO1). Latest data is imported before rollover (RO2). Learners who would sit in an active grade next year get a new school-learner row, grade plus one, class `unknown` (RO3). Learners who would not be in an active grade have programme associations ended on 31 December (RO4). New-year data whose effective date is before the configured "valid date" does not change learner records (RO6). Learner lists and login cards are not sent before that date (LL1). The valid date must be set every year by hand (LM1 note).

Provenance: **Documented** in `System Business Rules.xlsx`. No dedicated Lambda. **Requires Confirmation** against the converted procedures.

## Integrations

| System | Direction | What moves | Auth as evidenced | Failure behaviour |
|---|---|---|---|---|
| DDD FTPS | Inbound files | Three CSVs. Host, user, and password are written in `DDD and CEMIS Import.docx`. | Password in that document. Not repeated here. | Documented: job checks whether today's file is already imported. |
| DDD S3 | Inbound | Same three file names | Lambda role `click_lambda` on the staging serverless files | `ddd-get-files` does nothing useful unless the count is 3. |
| CEMIS via staff / Google Drive | Inbound CSV | Western Cape learners | Drive access is operational, not in `master` code. `origin/prod` has `get-file-from-gdrive` with `credentials.json` in git. | Documented: validation failure writes an error file and stops. |
| Reading Eggs | Outbound registration CSV; inbound usage and learner list | Extracts and usage | S3 import via `aws_s3`. SFTP is the documented source. | Export logs "copy failed". Documented polling notifies Blake if the usage file is missing. |
| Matific | Outbound registration | School and learner extracts. Procedures exist. | **Unknown** in code. Usage import **Planned** / "not done". | **Unknown** |
| Reflective Learning | Outbound registration | `pExtractReflectiveLearningRegistrationLearnerListf('South Africa')` | **Unknown** | **Unknown** |
| TAO | Outbound token request; inbound quiz results | OAuth client-credentials, then SQL inserts | Hardcoded client id and secret in two JS files | Axios errors are logged. |
| Mobenzi | Was inbound quiz results | **Documented** as retired | n/a | n/a |
| Secrets Manager | Inbound | `staging-aurora`, `dw-aurora` | Lambda IAM | Catch logs and continues. |
| SSRS / SSIS on EC2 | Lambdas start packages | Reports | SSM sample uses a hardcoded instance id | **Unknown** for the real package |
| Portals | Read `portal.*` and `ClickDW` | Learner redirect, identity | Not in this repo | **Unknown** |

## Constraints

- EMIS beginning with `1` is treated as Western Cape / CEMIS in `pgenerateusernames`. **Implemented**.
- DDD and CEMIS imports are independent; either may run first. **Documented**.
- Masterlist and environment transfers are previewed with action `R` before commit `C`. **Documented**.
- Phase 1 reporting stays on SSRS. **Documented** in the 10 May 2022 transcript.
- Matific and Reflective Learning usage imports were deferred. **Documented**.
- Lambdas are Node.js 14.x except `secrets-test` (Node.js 12.x), `get-secrets` (Python 3.8), `cemis-launch-reports` and `ddd-launch-import-learner-ddd-finalize-NOT-USED` (Python 3.9). Deployment stacks use provider runtime Python 3.7 because they only deploy CloudFormation. Region is `af-south-1`.
- No Lambda on `master` has a schedule event. Orchestration JSON is on `origin/prod` only.
- SQL still contains SQL Server constructs the converter could not translate (`@@TRANCOUNT`, explicit transactions). Those procedures can fail at runtime. **Implemented** source, behaviour **Unknown**.

## Requirements that must not be treated as confirmed

- The February 2022 workbook's Aurora MySQL, Glue, QuickSight, Amplify, and Terraform target.
- The 25% / proposed 10% school-activation threshold, until read out of `pactivateschoolreadingeggs`.
- That `origin/prod` Step Functions are what production runs.
- That a human still performs the DDD file-check gate. The `prod` state machine calls complete and finalise without a task state that waits for a person.
- Go-live, parallel-run result, and whether this stack is still in service.
