# 0001 — Notification settings fix converges via a new terminal update package (1.0.6)

- Status: Accepted
- Date: 2026-05-30
- Module: `stride_k12notifications`
- Ticket: CS-1767

## Context

The CS-1767 fix to the custom notification settings page is code-only (the
settings-page query, the grouping logic, and `js/settings.js`) — no schema or
language change. Three facts make its delivery non-trivial:

1. **Code-first deploy.** Module files reach an environment via git build/deploy;
   the `sys_modules` version then converges separately via Studio update packages
   (`manage.php`).
2. **Packages copy files.** Every `stride_k12notifications` update package sets
   `update_files = 1`, so running one copies its `source/` tree over the root
   module on disk.
3. **Stepwise updater.** `BxDolStudioUpdater` runs a package only when the
   installed version exactly equals its `version_from` (`:77`), then advances one
   step.

Per-environment installed versions at decision time:

| Env     | Installed (`sys_modules`) |
| ------- | ------------------------- |
| prod    | 1.0.2                     |
| DEV/QA  | 1.0.5                     |
| root FS | 1.0.5 (this branch)       |

**The regression trap.**
`update_1.0.2_1.0.3/source/classes/StrideK12NotificationsDb.php` still holds the
stale `alert_unit`-filtered query. On prod (1.0.2) the update chain runs that
package and copies the stale file *over* the freshly git-deployed fixed root
`Db.php`. Because no later package re-ships `Db.php`, prod would land on its
target version with the bug silently reintroduced.

## Decision

- **Bump the root module to 1.0.6.**
- **Create `update_1.0.5_1.0.6` as the new terminal package**, carrying the fixed
  `StrideK12NotificationsDb.php` and `StrideK12NotificationsModule.php` under
  `source/classes/`. `version_from = 1.0.5`, `version_to = 1.0.6`. Files-only: no
  SQL, no language diff.
- Add `sonar.cpd.exclusions` entries for the new package's mirrored paths
  (required by module-standards for every update package).
- `js/settings.js` is **not** mirrored into any package; the git code-deploy
  delivers it on every environment and no package overwrites it.

Convergence:

- **DEV/QA (1.0.5)** run `1.0.5_1.0.6` → 1.0.6 with the fix.
- **prod (1.0.2)** chains `1.0.2_1.0.3 → 1.0.3_1.0.4 → 1.0.4_1.0.5 → 1.0.5_1.0.6`
  → 1.0.6. The terminal package re-ships the fixed `Db.php`/`Module.php` *last*,
  overwriting the stale `1.0.2_1.0.3` copy. The trap is defused by
  last-writer-wins.

## Consequences

Positive:

- Clean version semantics: 1.0.5 stays the buggy/historical build, 1.0.6 is the
  fixed build — no ambiguity about "which 1.0.5."
- Every environment converges through a tracked package, including DEV/QA.
- One place to reason about (the terminal package) instead of per-file
  archaeology across the chain.

Costs / ongoing constraints:

- Prod must run **all** update steps through to 1.0.6; stopping mid-chain (e.g. at
  1.0.3) leaves it transiently on the stale `Db.php` until the terminal step.
- The fixed `Db.php`/`Module.php` are duplicated into the package `source/`
  (intentional UNA convergence) — covered by the new `sonar.cpd.exclusions`.
- **Invariant for future changes:** the package that is the terminal step for an
  upgrading tenant must carry the current fixed copy of every file that any
  *earlier* package ships a stale copy of. A later version bump must move these
  files into the new terminal package (or otherwise guarantee no stale earlier
  copy is the last writer).

## Alternatives considered

- **Mutate 1.0.5 in place (no bump).** Add fixed files to
  `update_1.0.4_1.0.5/source` and let the git deploy fix DEV/QA. Rejected: it
  redefines the contents of a version already deployed to DEV/QA, leaving "1.0.5"
  ambiguous.
- **Per-file mirroring** (fix `1.0.2_1.0.3/source/Db.php` and
  `1.0.3_1.0.4/source/Module.php` in place). Works for prod, but requires tracking
  the last-writer package per file — fragile and error-prone. Rejected.
- **Runtime guard / no package change.** Not applicable: the trap is a file-copy
  artifact of the update step, not a runtime-state problem; runtime DDL/file
  mutation is prohibited regardless.
