Trust Center

Change Management Policy

How changes to code, infrastructure, and procedures are proposed, reviewed, and deployed.

Effective: 2026-05-21Owner: Howell & Gibbs LLCStatus: v1 — initial publicationDownload PDF

This policy describes how changes — to application code, infrastructure configuration, third-party integrations, and operational procedures — are proposed, reviewed, deployed, and audited at SendTax.

It is a companion to our Information Security, Access Control, Incident Response, and Business Continuity policies.

Items marked Roadmap are not yet in production.


1. Principles

  1. Changes go through CI. No change reaches production without passing automated checks. There is no "trusted bypass" — including for the founders.
  2. Two stages, every time. Production deploys are preceded by a staging deploy of the same artifact. Staging is not a coincidence of trunk-based development; it is a deliberate gate.
  3. Reviewable history. Every change is traceable to a pull request, a commit, an author, and a CI run. The audit trail is the same surface engineers work in — not a separate ticket system that drifts from reality.
  4. Reversible by default. Every change should have a thought-out rollback. Database migrations are the special case (§ 9), but even then we deploy with the rollback in mind.
  5. Honest about scale. SendTax operates with a two-person team. This policy reflects the controls that are achievable, audited, and enforced at that scale, not the formal-CAB process of a 200-person company.

2. Scope

This policy applies to:

  • Application code — frontend (Next.js apps) and backend (FastAPI + Celery worker).
  • Infrastructure as codefly.toml files, GitHub Actions workflows, Dockerfiles, provisioning scripts under st-backend/infra/.
  • Database schema — Alembic migrations.
  • Operational configuration — Fly.io secrets, third-party service configuration accessed through the relevant providers' consoles.
  • Operational procedures — runbooks, internal documentation used in incident response and routine operations.
  • Trust Center documents — these are themselves treated as changes that flow through the same review process.

It does not govern routine operator actions (a single deploy, a secret rotation) that are themselves applications of an existing documented procedure.


3. Change categories

Every change is one of four categories. Category drives the path it takes through this policy.

CategoryDefinitionPath
StandardAny planned change to code, infrastructure, or schema in the normal flow of work.§ 5 standard flow.
Pre-approvedRecurring routine changes documented as procedures (secret rotation, dependency updates, deploy to staging from an approved branch).§ 5 standard flow with abbreviated review.
EmergencyA change required to contain or remediate an active incident, where waiting for the full standard flow would cause material harm.§ 10 emergency flow.
Trust Center / policyA change to a Trust Center document or other published policy.§ 5 standard flow with the policy owner as required reviewer.

4. Roles and responsibilities

RoleResponsibility
AuthorThe person making the change. Responsible for the design, the implementation, the tests, and the rollout plan.
ReviewerThe person reviewing the change. Required for non-trivial changes; see § 6.
DeployerThe person triggering the production deploy. May be the author for standard changes.
Owner of the affected areaFor changes touching critical paths (encryption, RLS, billing, schema), the area owner reviews even if a different reviewer was already assigned.

In a two-person team, the author and reviewer are usually the only two people involved. § 6.2 describes how that single-reviewer constraint is mitigated.


5. The standard change flow

Every standard change moves through these stages, enforced by GitHub Actions and our deploy workflows:

5.1 Branch and commit

  • Work happens on a feature branch off main. Direct commits to main are not permitted.
  • Commit messages describe what changed and why, not just what was touched.

5.2 Open a pull request

  • Pull requests target main.
  • The PR description states the user-facing intent, the testing approach, any operational risk, and the rollback plan if non-obvious.

5.3 Local pre-commit gate

Before a PR is opened, the author's local pre-commit hooks run:

  • Format and hygiene checks (trailing whitespace, large files, merge-conflict markers, debug statements left in code).
  • make check-fast for backend changes — runs the same fast lint/type checks that CI enforces.
  • An end-to-end gate check for both backend and frontend tier-1 paths.

The pre-commit hooks are defined in .pre-commit-config.yaml and in the frontend's st-apps/.husky/pre-commit.

5.4 Continuous integration

Every PR (and every push to main) triggers CI workflows scoped to the changed area by paths: filters:

Backend (.github/workflows/backend-ci.yml):

  • lint-typeruff (linter) and mypy (type checker). The workflow is explicitly designed to mirror make check-fast so that a git push --no-verify cannot bypass the lint and type gate.
  • tests — full pytest suite, running against a PostgreSQL service container.

Frontend (.github/workflows/frontend-ci.yml):

  • pnpm -w lint
  • pnpm -w type (TypeScript type checking)
  • pnpm -w build (full monorepo build)
  • Lockfile discipline — CI fails if pnpm-lock.yaml is mutated unexpectedly during a CI run.
  • OpenAPI client drift check — the frontend's generated API client must match the backend's published OpenAPI schema.
  • Monorepo test suite.
  • Optional Playwright E2E suite, gated by repository secrets.

A failing CI run blocks merge. There is no documented procedure to bypass CI; we treat that as a policy invariant.

5.5 Review

Per § 6 below.

5.6 Merge

Merges to main use squash-merge by default for application code. The merge commit message preserves the PR title and description.

5.7 Staging deploy

A push to main automatically triggers:

  • backend-deploy.yml (staging environment) — builds and deploys to st-api-stg and st-worker-stg.
  • frontend-deploy-web.yml (staging environment) — deploys the Fly-hosted frontend apps to their staging variants.

Alembic migrations run as Fly's release_command before new machines serve traffic; a migration failure aborts the deploy and keeps the old machines healthy.

5.8 Production deploy

Production deploys are manual and one path only. The release.yml workflow ("Release to Production") is the single sanctioned entry point. It calls backend-deploy.yml and frontend-deploy-web.yml with environment: production.

Inside the deploy workflow, a job gate enforces that no other trigger (no workflow_run, no direct workflow_dispatch of the deploy workflow) may set environment=production. This is a real, code-level guarantee in .github/workflows/backend-deploy.yml.

The deploy order is:

  1. Backend. Migrations apply first (via release_command), then API and worker machines roll. Backend goes first because schema additions may be prerequisites for the new frontend.
  2. Fly-hosted frontends. st-filer-web, st-pro-web, st-admin-web.
  3. Vercel-hosted marketing site. Fast-forwards the deploy branch to the released SHA, which the st-www project auto-deploys.

Each stage is rolled to staging and re-validated before its production counterpart proceeds.


6. Code review

6.1 What review covers

A reviewer is expected to read the change for:

  • Correctness. Does the code do what the PR claims?
  • Safety. Any risk to encryption boundaries, RLS, authentication, or audit-log integrity?
  • Tests. Are there tests for the new behavior, and do they actually exercise the path?
  • Operational fit. Migrations safe? Secrets handled correctly? Rate limits sensible? Errors logged?
  • Trust Center alignment. Does the change require an update to any published policy or Sub-processors page?

6.2 Who reviews

  • Default. A reviewer other than the author. The current team is two people; in practice this means the other operator.
  • Self-merge exception. For genuinely trivial changes (typo fixes, documentation-only edits, dependency bumps already signed off by automated scanning) the author may self-merge. Self-merges are recorded in the PR for audit.
  • Roadmap. As the team grows, we will move to a model where every non-emergency change requires a non-author reviewer with no self-merge exception. (Roadmap.)

6.3 Sensitive-area reviews

Changes to any of the following require explicit acknowledgment from the area's owner in the PR before merge, in addition to any generic review:


7. Testing requirements

  • Unit tests are expected for new application logic.
  • Integration tests are required for changes touching the encryption module, RLS policies, audit-log hardening, and cross-tenant access paths. Existing integration tests cover these areas and a change is expected to extend rather than remove them.
  • Migration tests — schema changes that affect existing data must include a backfill plan and, where feasible, a test that exercises the backfill against a representative dataset.
  • Manual testing of UI changes happens in staging before production release. The release runbook makes staging validation an explicit prerequisite.

CI failure is sufficient to block merge; CI passage is not sufficient to authorize merge. A reviewer's judgment about test coverage is also part of the gate.


8. Infrastructure and configuration changes

Infrastructure and configuration changes follow the same flow as code changes:

  • fly.toml changes are committed to source control and reviewed in PRs.
  • GitHub Actions workflow changes trigger a workflow-file validator in the frontend CI suite, in addition to standard review.
  • Secrets are set via fly secrets set or via the provisioning scripts under st-backend/infra/, with the secret values themselves sourced from environment- scoped 1Password vaults. Changes to which secrets exist (the schema, not the values) flow through PRs.
  • Direct console changes to third-party providers (Cloudflare, Clerk, GCP, etc.) are minimized; where they cannot be avoided, the operator records the change in an internal change log so the audit trail is preserved outside source control. (Roadmap: tighten this to a structured change-log entry per provider-side change.)

9. Database migration discipline

Database changes are the highest-risk category and have their own runbook at st-backend/docs/ops/MIGRATIONS.md. The policy-level commitments are:

  • Migrations are forward-only by default. We do not expect to run alembic downgrade against production. Recovery from a broken schema change uses PITR restore plus a corrective migration, not a downgrade.
  • Migrations run as Fly's release_command before new machines serve traffic. A non-zero exit aborts the deploy and keeps the old machines healthy.
  • CREATE INDEX CONCURRENTLY and other non-transactional operations are flagged in code review because they commit incrementally and partial state is possible on failure.
  • Backfills of existing rows are written to be idempotent and run incrementally where the dataset is large.
  • Dual-deploy pattern for breaking schema changes: deploy additive change → backfill → deploy code that uses the new shape → deploy cleanup of the old shape. The runbook describes this pattern in detail.

10. Emergency changes

An emergency change is one that must skip part of the standard flow because waiting would cause material harm — typically during an active Critical or High-severity incident (see Incident Response Policy).

10.1 What is permitted

During an emergency, the following may be relaxed:

  • Pre-merge review. A change may be merged without prior reviewer approval if the Incident Commander authorizes it.
  • Test coverage. A change may ship without tests for the new behavior if the corrective action is time-critical.

10.2 What is NOT permitted, even in an emergency

The following always apply:

  • CI must pass. No emergency justifies merging code that fails lint, type checks, or existing tests.
  • The staging → production sequence stands. Even an emergency production deploy runs through the staging gate; this typically takes only a few minutes and we do not skip it.
  • release.yml remains the only path to production. Direct production dispatch is not permitted.
  • Secrets are not committed to source control. Always.

10.3 After the emergency

Every emergency change generates a follow-up obligation:

  • A post-emergency PR adds the missing tests, refines the hotfix into a clean change, and updates documentation as needed.
  • The emergency itself is documented on the incident timeline per the Incident Response Policy.
  • The post-incident review evaluates whether the emergency shortcut was justified and whether the standard flow needs adjustment to avoid the same shortcut next time.

11. Rollback

Every change must be rollback-able. Specifically:

  • Application code rollback is performed by deploying the previous image tag through the same backend-deploy.yml or frontend-deploy-web.yml workflow.
  • Frontend rollback (Vercel) is performed by reverting the deploy branch to the prior SHA and triggering an auto-deploy.
  • Schema rollback uses PITR restore plus a corrective forward-migration. We do not rely on Alembic downgrade scripts to rescue production.
  • Configuration rollback uses git revert on the relevant fly.toml or workflow file, followed by a deploy.

The deploy runbook in st-apps/docs/RUNBOOKS.md documents the basic deploy/rollback procedures.


12. Audit trail

The audit trail for changes is built into our tools, not maintained separately:

  • Git history. Every change has an author, a timestamp, a PR reference, and a commit message.
  • Pull-request history. Reviews, approvals, and comments are preserved by GitHub.
  • CI history. Every check, every run, every failure is preserved in GitHub Actions for the duration of GitHub's default retention.
  • Deploy history. Fly retains deploy records (image, who triggered, outcome) and is queryable via fly releases.
  • Application audit log. Sensitive actions inside the running application are recorded in the audit_log table per the Access Control Policy § 8.

Together these provide a defensible record of who changed what, when, why, and with whose approval.


13. Documentation

A change is not complete until the relevant documentation is updated:

  • Operational runbooks (st-apps/docs/RUNBOOKS.md, st-backend/docs/ops/MIGRATIONS.md) are updated when a procedure changes.
  • CLAUDE.md is updated when the high-level architecture changes meaningfully.
  • Trust Center documents are updated when a change affects any published commitment (sub-processors, security posture, data flows).

Documentation updates ride in the same PR as the change, not in a separate "follow-up" PR.


14. Periodic review

This policy is reviewed at least annually and additionally whenever:

  • A real incident reveals a flow gap.
  • A material change to our CI, deploy, or hosting architecture ships.
  • The team size changes in a way that affects the review model in § 6.

Material changes are noted in the version line at the top of this document.


15. Contact

Security disclosures[email protected]
Change-management questions[email protected]
General[email protected]
Operating entityHowell & Gibbs LLC