Skip to content

docs(governance): add code freeze policy for TeachLink Backend (#1840) - #1919

Merged
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
eriifeomoniyi-hue:fix/issue-1840-add-code-freeze-policy
Oct 17, 2026
Merged

RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
eriifeomoniyi-hue:fix/issue-1840-add-code-freeze-policy

Conversation

@eriifeomoniyi-hue

Copy link
Copy Markdown
Contributor

Add a code freeze policy for TeachLink Backend

Closes #1840

Type Governance documentation (policy addition)
Scope 2 files · +236 lines · all inside Governance/
Code impact None — no TypeScript, config, schema, dependency, or CI changes
Status Ready for review

TL;DR

Issue #1840 identified a governance gap: the Governance/ tree had versioned policies
for release branching, branch protection, approval requirements, backports, review
SLAs, and embargoes — but no document defining a code freeze. This PR adds
Governance/policies/CODE_FREEZE.md, the single versioned reference that answers
three questions contributors and maintainers were previously answering ad hoc:
when does a freeze apply, what may still land during one, and how is an exception
granted?
The addition is recorded in Governance/CHANGELOG.md. Exactly two files
change, both inside Governance/.


1. Motivation and background

  • The gap. Governance/policies/CODE_FREEZE.md did not exist. The only mention of
    a freeze anywhere in the governance tree was a dangling passing reference in
    Governance/policies/COMMUNICATION_NORMS.md §4.3 — "Pull requests targeting main
    during a release freeze"
    — which pointed at no definition, no declaration procedure,
    and no exception path.
  • Why it matters. Freezes did happen in practice: the release process cuts
    release/x.y.z branches (RELEASE_BRANCHING.md), gates merges behind sign-offs
    (processes/RELEASE_SIGNOFF.md), freezes non-remediation deployments when an error
    budget is exhausted (domains/SLA_SLO.md), and holds branches during security
    embargoes (EMBARGO.md). Without a freeze policy, each of those moments produced
    inconsistent expectations: contributors didn't know whether to open PRs, reviewers
    didn't know what to accept, and there was no defined way to grant an exception or a
    record when one was granted.
  • The ask. Issue Add a code freeze policy for TeachLink Backend #1840 requested the document, the change self-contained within
    the Governance/ folder, with a maximum scope of two files.

2. What a code freeze means in this policy

A code freeze is a time-boxed period during which changes to the repository are
restricted so that a release candidate, a maintenance window, or a security response
stays stable and verifiable.

Two design invariants underpin the whole document:

  1. A freeze must be declared to exist. Silence is not a freeze. §1.2 requires an
    announcement naming scope, reason, trigger, start, expected end (or ending event),
    approver, and owner — posted as a pinned GitHub issue, echoed on open PRs, and
    mirrored to the community channels in COMMUNICATION_NORMS.md. This makes freeze
    status discoverable rather than folklore.
  2. A freeze never lowers the bar. §2.3 explicitly preserves every required check
    and approval from APPROVAL_REQUIREMENTS.md §2. Freezes restrict what lands; they
    do not relax how it is reviewed, and they never authorise bypassing branch
    protection.

3. Contents of Governance/policies/CODE_FREEZE.md

Metadata header (Status Active · Version 1.0.0 · Owner Maintainer Team, with the release manager · Last reviewed 2026-10-17 · Review cadence every 6 months ·
applies-to plus a Related row linking ten sibling documents), then:

# Section What it specifies
1 When a freeze applies §1.1 trigger table — release stabilization (cut of release/x.y.z → tag on main), release sign-off (start of gates → recorded decision), security embargo (per EMBARGO.md), incident/degraded service (declaration → all-clear), error-budget exhaustion (per domains/SLA_SLO.md/MIGRATION_ROLLBACK.md), scheduled maintenance — each with declaring role and default window. §1.2 declaration lifecycle: who may declare, mandatory declaration contents, announcement channels, retroactive effect on already-open PRs, amendments/extensions, and the rule that normal merging resumes only after the freeze is formally lifted.
2 What is allowed during a freeze §2.1 permitted-changes table: regression/defect fixes, security fixes (VULN_SLA.md + EMBARGO.md), release-blocking fixes, documentation & comments, change-record updates (release notes, changelog, decision log), reverts, and CI/tooling hotfixes needed to keep required checks green. §2.2 prohibited list: features, refactoring/"while-I-were-here" cleanups, dependency bumps (security exempt), unnecessary schema/migration work, protection-rule edits, force-push/deletion of branches, and any merge that bypasses required checks or approvals. §2.3 conditions for every landing change: the PR names its freeze trigger and justification, all required checks/approvals still apply, the diff is the smallest that satisfies the purpose, the merge strategy per MERGE_STRATEGY.md is used, and the freeze owner/release manager performs the merge — plus the review-SLA carve-out delegated to COMMUNICATION_NORMS.md §4.3 (acknowledgement within one business day by the freeze owner).
3 Exception process §3.1 approver matrix by exception kind — release-blocking → release manager (final release authority stays with the lead maintainer per processes/RELEASE_SIGNOFF.md §4); incident → incident commander and a maintainer; routine feature/cleanup → two maintainers including the freeze owner; embargoed security fixes → route through EMBARGO.md with no separate exception needed; repository-wide/indefinite → lead maintainer. Self-approval only when no independent approver exists, and then it must be re-reviewed. §3.2 required request contents (PR link, trigger & scope, category + cost of delay, risk/rollback/verification, requested approvers). §3.3 decision rules: 1 business day (4 hours for incident-driven), approval recorded with approver/rationale/limits, denials state what would make it approvable later, approvals are single-PR-scoped never categorical. §3.4 recording in DECISION_LOG.md within 24 h (mirroring APPROVAL_REQUIREMENTS.md §5.3), expiry at freeze end or stated expiry (landed changes are not rolled back on expiry), disputes via processes/ESCALATION_PATH.md to the lead maintainer.
4 Enforcement Branch protection remains the mechanism (already-prohibited direct/force pushes, unchanged required checks); the freeze: exception label from LABEL_TAXONOMY.md marks approved exceptions; non-compliant merges are reverted or handled as override violations under APPROVAL_REQUIREMENTS.md §5 and noted in the decision log; backports defer to BACKPORT.md (eligibility §3).
5 Roles and responsibilities Release manager, maintainers, incident commander, contributors, lead maintainer — one row each.
6 Regression tests where applicable States the documentation-only rationale (no runtime behaviour ⇒ no new test applicable), and enumerates the test obligations that bind if the policy is ever implemented in code/CI: permitted-vs-prohibited classification, approved/expired/absent exception handling, and never-skipping required checks because of a freeze. Also names the existing gates that stay authoritative meanwhile.
7 Documentation of change Rationale (issue #1840), scope statement, and the versioned change log row (1.0.0 · 2026-10-17).

4. Design decisions and rationale

# Decision Why
D1 A standalone CODE_FREEZE.md rather than amending RELEASE_BRANCHING.md Issue #1840 asked for exactly this path; freezes also arise outside releases (incidents, embargoes, budget exhaustion, maintenance), so release branching wasn't the natural home for all of them.
D2 Explicit declaration model — "silence is not a freeze" The gap wasn't the concept of freezing but its discoverability. Making the declaration mandatory and channel-specified turns freeze status from tribal knowledge into an auditable artefact.
D3 Freeze never lowers required checks/approvals Consistency with BRANCH_PROTECTION.md ("Overrides… may not disable branch protection rules") and APPROVAL_REQUIREMENTS.md §5.2. A freeze that weakened review would trade one risk (unstable release) for another (unreviewed code).
D4 An approver matrix, not a single approver Freezes come from six triggers with different owners (release manager, incident commander, security response, lead maintainer). A single approver would create a bottleneck during exactly the incidents when speed matters; the matrix keeps each freeze with the role that owns its trigger.
D5 Exceptions are single-PR-scoped and logged in DECISION_LOG.md within 24 h Prevents approvals from silently becoming policy. Logging mirrors the existing override-recording rule (APPROVAL_REQUIREMENTS.md §5.3) so emergency overrides and freeze exceptions appear in one audit trail.
D6 Enforcement reuses branch protection and existing labels/checks; no CI changes The ≤2-file / Governance/-only constraint rules out workflow edits; branch protection already blocks force-pushes, direct pushes and missing approvals, which is the enforcement a freeze actually needs on day one.
D7 Cross-reference, do not restate Branching, approvals, backports, merge strategy, embargoes, escalation, decision-log format, and SLA carve-outs are each owned by an existing document. The policy cites them so a change in one document never silently contradicts the freeze policy.

5. Relationship to existing governance documents

Document Owns How this policy interacts
policies/RELEASE_BRANCHING.md main/develop/release/* model, cut/stabilize steps Supplies the release stabilization trigger window (§1.1)
processes/RELEASE_SIGNOFF.md Sign-off gates, final release authority Supplies the release sign-off trigger; §3.1 defers final authority to it (§4)
policies/BRANCH_PROTECTION.md Protected branches, required checks, override limits Enforcement mechanism (§4); never weakened
policies/APPROVAL_REQUIREMENTS.md Minimum approvals per change type, override rules/recording Approval bar preserved (§2.3); violation handling + 24 h recording mirror §5/§5.3 (§3.4, §4)
policies/EMBARGO.md Embargo durations, embargo list, early disclosure Embargo freezes and fixes-route (§1.1, §3.1)
policies/VULN_SLA.md Severity → remediation clocks Security fixes are always permitted freeze-scope (§2.1)
policies/BACKPORT.md / policies/MERGE_STRATEGY.md Backport eligibility / merge mechanics Deferred to, not restated (§4, §2.3)
policies/COMMUNICATION_NORMS.md §4.3 Review-SLA exemptions during a freeze Previously the only freeze mention; now the policy defines what §4.3 points at
domains/SLA_SLO.md, domains/MIGRATION_ROLLBACK.md Budget-exhaustion deployment freezes, rollback discipline Error-budget + rollback triggers (§1.1)
domains/ENV_PROMOTION.md Promotion gates, maintenance/operator changes Scheduled-maintenance trigger + migration gating (§1.1, §2.2)
DECISION_LOG.md Decision-record format and custody Exception log destination (§3.4)
LABEL_TAXONOMY.md Label definitions freeze: exception label (§4)
roles/RELEASE_MANAGER.md Release-manager duties and limits Named release-freeze owner/approver (§1.1, §3.1, §5)
processes/ESCALATION_PATH.md Escalation tiers/timelines Freeze dispute escalation (§3.4)

6. Alternatives considered (and rejected)

  • Extend RELEASE_BRANCHING.md with a short freeze section — rejected: leaves
    incident/embargo/maintenance freezes undocumented, and issue Add a code freeze policy for TeachLink Backend #1840 explicitly asked
    for Governance/policies/CODE_FREEZE.md.
  • Define freezes in CONTRIBUTING.md — rejected: Governance/README.md reserves
    CONTRIBUTING.md for contribution mechanics; freeze semantics are governance
    policy, and touching CONTRIBUTING.md would break the "Governance/ only"
    constraint anyway.
  • Automate enforcement now (CI rules that block freeze-prohibited paths) —
    rejected for this PR: it would require changes outside Governance/. §6 instead
    pre-specifies the regression tests any future automation must ship with, so the
    policy and its eventual implementation stay in lockstep.
  • A stub file saying "freezes are announced when they happen" — rejected: it
    would technically create the file while leaving contributors no better informed;
    the issue asks for a document that "documents when freezes apply / specifies what
    is allowed / defines the exception process", so all three are written out.

7. Files changed (scope = 2, all inside Governance/)

File Change Size
Governance/policies/CODE_FREEZE.md Added — the policy +235
Governance/CHANGELOG.md Modified — one bullet under ## [Unreleased] → ### Added, citing issue #1840 +1

git diff main --stat →
2 files changed, 236 insertions(+), working tree clean, zero paths outside
Governance/
. (The pnpm-lock.yaml churn produced by dependency installation
during CI reproduction was reverted before staging and is not part of this branch.)

8. Acceptance criteria → evidence

Criterion (#1840) Evidence
Governance requirement implemented §1–§7 of the new document cover freeze timing, allowed changes, exceptions, enforcement, roles, tests, and change documentation (§3 above maps each requirement to its section)
Scope ≤ 2 files git diff main --stat: exactly 2 files
No changes outside Governance/ Diff paths: Governance/CHANGELOG.md, Governance/policies/CODE_FREEZE.md only; git status --porcelain clean
No regression in existing functionality Documentation-only; Governance/ is outside the Jest root (jest.config.js → rootDir: 'src'), outside the TS build (tsconfig.build.json compiles src/), excluded by .dockerignore's *.md rule, and invisible to the CI build-artifacts guard
Tests pass / project standards Full local CI green (§9), 22/22 unit tests pass (§10), commit conforms to @commitlint/config-conventional, doc style matches sibling policies (`
Change documented Governance/CHANGELOG.md entry + §7 of the policy, both citing #1840

9. Local CI verification (same steps as .github/workflows/ci.yml)

Environment: Node 24 · pnpm 9 · postgres:16-alpine container with CI's
DATABASE_* and NODE_ENV=ci variables. Exit codes captured directly from each
command.

# CI step (workflow name) Command run Result
1 No build artifacts in git git ls-files 'dist/' '*.tsbuildinfo' 'compliance/reports/' ✅ empty → guard passes
2 Run lint pnpm run lint:ci ✅ exit 0
3 Check TypeScript errors pnpm run typecheck ✅ exit 0
4 Build application pnpm run build ✅ exit 0
5 Check migrations use only the passed queryRunner pnpm run migrations:check ✅ exit 0 — "All 53 migration files use only the passed queryRunner"
6 Run migrations pnpm run migration:run ✅ exit 0
7 Check for schema drift pnpm run migration:generate:check ✅ exit 0 — "No schema drift"
8 Revert migrations pnpm run migration:revert ✅ exit 0
9 License compliance scan npm run license:scan ✅ exit 0 (tool reports Status: WARNING — pre-existing conditions, exit code still 0)
10 Audit dependencies pnpm audit --audit-level=high ⚠️ exit 1 — 170 findings (14 low · 70 moderate · 82 high · 4 critical). Pre-existing and matching CI's own posture: the workflow step is continue-on-error: true with a comment stating this gate activates only after the inherited CVEs are remediated. Unrelated to a Markdown-only change (lockfile untouched on this branch).

Post-change validations run against the final committed files:

  • Link integrity: all 36 relative links in CODE_FREEZE.md resolve on disk
    (programmatically extracted and checked; the document has no external or
    anchor-only links).
  • Cross-reference integrity: every cited section exists in the referenced
    document — COMMUNICATION_NORMS.md §4.3, APPROVAL_REQUIREMENTS.md §§2/5/5.3,
    MERGE_STRATEGY.md §1, BACKPORT.md §3, DECISION_LOG.md entry format,
    processes/RELEASE_SIGNOFF.md §4.
  • Style: npx prettier --check 'Governance/**/*.md' flags 135 of 135
    Markdown files including every pre-existing policy, and CI's format:check covers
    only src/** and test/** — i.e. Markdown formatting is not enforced anywhere in
    this repository. The new file deliberately follows the compact table style of its
    siblings rather than introducing a one-off formatting convention.

10. Tests

  • Passing unit slice (representative, DB-free):
    npx jest src/i18n/i18n.build.spec.ts src/rate-limiting/dto/update-rate-limiting.dto.spec.ts src/incident-management/dto/incident-resolution.dto.spec.ts src/rate-limiting/services/quota-tracking.service.spec.ts --forceExit --runInBand
    → 4 suites passed, 22/22 tests passed.
  • Pre-existing failure, documented honestly: pnpm run test:sanity fails during
    suite load with SyntaxError: Unexpected token 'export' while Jest imports
    otplib → @otplib/plugin-base32-scure (an ESM package Jest's default transforms
    don't handle). I reproduced the identical failure with this change stashed
    (git stash -u onto clean main), proving it predates this PR. The project's CI
    workflow contains no Jest job, and this branch changes no code under src/, so
    there is no causal path from this change to that failure. Fixing it belongs in a
    separate code PR (it would violate this issue's ≤2-file / Governance/-only
    constraint here) — flagged as follow-up rather than papered over.
  • Why no new automated test accompanies this PR: the policy is Markdown with no
    runtime behaviour; Governance/ is excluded from the Jest root entirely. This is
    the same rationale the repository already documents for governance-only changes
    (processes/RELEASE_SIGNOFF.md §5, templates/RELEASE_CHECKLIST.md
    "Regression Tests Where Applicable"). §6 of the new policy goes one step further
    and pre-registers the three regression assertions any future code/CI
    implementation of this policy must ship with, so adding policy machinery later
    can't land untested.

No check was skipped, weakened, or suppressed, and no assertion, lint rule, or type
check was modified anywhere in this PR.

11. Documentation of the change

  • Changelog: Governance/CHANGELOG.md → [Unreleased] → Added bullet naming the
    file, summarising its coverage (triggers, declaration, allowed/prohibited changes,
    exception process, enforcement, roles, regression-test guidance), and citing
    issue Add a code freeze policy for TeachLink Backend #1840. Format follows Keep a Changelog, matching existing bullets.
  • In-document change history: §7 + the change-log table (Version 1.0.0 ·
    2026-10-17) so the policy is versionable on its own terms, matching
    RELEASE_BRANCHING.md, BRANCH_PROTECTION.md, VULN_SLA.md, and EMBARGO.md.
  • Review cadence: header declares a 6-month review, consistent with the other
    policies.

12. Rollout and communication

  1. On merge, the policy is immediately in effect for all future freezes — no code or
    configuration switch is involved.
  2. The first freeze declared after merge must use the §1.2 declaration format; the
    freeze owner posts the pinned issue and mirrors it to the channels named in
    COMMUNICATION_NORMS.md.
  3. Exception requests reference §3.2's checklist and land in DECISION_LOG.md,
    giving maintainers their first auditable exception trail for the next
    transparency report (policies/TRANSPARENCY_REPORTS.md).
  4. Re-review is scheduled via the header's 6-month cadence, or earlier if a release,
    incident, or branch-protection process changes materially.

13. Security, compliance, and operational considerations

  • No executable change: zero TypeScript, configuration, schema, dependency, or
    workflow edits — nothing to review for injection, auth, or data-handling risk.
  • Embargo-safe by design: §2.1 makes embargoed security fixes permitted
    freeze-scope and §3.1 routes their exceptions through EMBARGO.md, so a freeze can
    never delay a fix for an embargoed vulnerability or pressure anyone into premature
    disclosure.
  • Access control preserved: §4 relies on branch protection rather than any new
    privileged path; §3.1 forbids unaudited self-approval when an independent approver
    exists; §3.4's 24-hour decision-log requirement keeps exceptions accountable.
  • No secrets or sensitive data appear in either changed file.

14. Known limitations and deliberate omissions

  1. Governance/README.md policy index not updated — it still lists only policies
    up to the earlier batches; adding a CODE_FREEZE.md row would be a third file and
    violate the issue's ≤2-file cap. Recommended as an immediate follow-up.
  2. No automated enforcement yet — freeze rules are enforced socially plus the
    pre-existing branch-protection gate; CI path-blocking would need changes outside
    Governance/. §6 pins the tests required when that happens.
  3. test:sanity fails on main today (ESM interop, evidence in §10) — out of
    scope here.
  4. pnpm audit is non-clean (§9, step 10) — pre-existing upstream CVEs the
    project has explicitly gated behind continue-on-error.
  5. Markdown formatting differs from prettier output (§9) — true of all 135
    Governance Markdown files and never enforced by CI.

15. How to review this PR

  1. Read Governance/policies/CODE_FREEZE.md top to bottom (~235 lines,
    self-contained) — check §1 against the release/embargo/SLO documents it cites,
    §2 for anything the project would realistically want prohibited-but-needed, and
    §3's approver matrix against your team's actual availability.
  2. Skim the one-line Governance/CHANGELOG.md addition.
  3. Confirm scope: git diff main --stat shows the same two files.

16. PR checklist

  • Links issue Add a code freeze policy for TeachLink Backend #1840 (Closes #1840 above; issue # cited in both changed files)
  • ≤ 2 files changed; every path inside Governance/
  • No changes outside Governance/
  • Policy documents when freezes apply, what is allowed, and the
    exception process
  • Regression tests addressed per "where applicable", with future obligations
    pre-registered in §6
  • Change documented (Governance/CHANGELOG.md + in-document change history)
  • All locally reproducible CI checks executed and green (§9); no check skipped
    or suppressed
  • Conventional, commitlint-compliant commit message
  • No application code, configuration, schema, dependency, or CI changes

Closes #1840

…code#1840)

Add Governance/policies/CODE_FREEZE.md: the versioned reference for code
freezes that the Governance folder was missing. It defines when a freeze
applies (release stabilization, release sign-off, security embargo,
incident, error-budget exhaustion, scheduled maintenance) and how one is
declared and ended, what is allowed and prohibited during a freeze, the
exception process (who approves, how to request, decision timelines,
recording and expiry), enforcement, roles, and regression-test guidance.

Record the addition in Governance/CHANGELOG.md. Scope is deliberately two
files, both inside Governance/, per issue rinafcode#1840; no application code,
configuration, schema, or dependency is touched.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@eriifeomoniyi-hue Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project

@RUKAYAT-CODER
RUKAYAT-CODER merged commit 192301f into rinafcode:main Oct 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a code freeze policy for TeachLink Backend

2 participants