Trust Center

Change Management Policy

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

Effective: 2026-05-21Owner: Howell & Gibbs LLCDownload 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 has one engineer. This policy describes the controls that are achievable at that scale, and says plainly where a control is a practice rather than a technical gate. It is 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 code — fly.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.

With one engineer, the author, reviewer, and deployer are usually the same person. § 6.2 describes what stands in for a second reviewer.


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 checks before merge

The repository defines local pre-commit hooks for format and hygiene checks, the fast lint and type checks CI runs, API-client regeneration, and a tier-1 end-to-end gate. They are defined in .pre-commit-config.yaml and the frontend's st-apps/.husky/pre-commit.

In practice the hook installed on the engineer's machine is advisory: it prints which local checks the staged change calls for and never blocks a commit. The engineer runs make check in the affected codebase before merging, and the longer local suite (make qa-overnight) before a release. The tier-1 end-to-end gate has not fired since the two codebases were merged into one repository in May 2026. (Roadmap: a blocking local gate, and a repaired tier-1 gate.)

5.4 Continuous integration

Every PR triggers a lean set of CI checks, scoped to the changed area by paths: filters. The heavier checks (integration tests, dependency audits, smoke E2E) run once a day in the full run described in § 5.7:

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

  • lint-type — ruff (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 — unit tests on every PR. The integration suite, which runs against PostgreSQL, and pip-audit run in the daily full run.

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.

Our GitHub plan does not offer branch protection, so a failing CI run does not technically block a merge. Merging only on green is the rule, and it is an operator practice rather than a technical control.

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

Once a day (05:00 UTC), if main has moved, daily-staging.yml runs the full CI suite and, only if it passes:

  • 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.
  • The staging end-to-end gate against the live staging apps.

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

SendTax has one engineer, who writes, reviews, and merges almost every change. In practice:

  • Self-merge is the norm. Most pull requests, including sensitive-area changes, are merged by their author with no second human reviewer. Each PR's merge record shows who merged it, so every self-merge is on the record.
  • What stands in for a second reviewer. Every change goes through a pull request and CI (§ 5.4), and AI-assisted review: much of the code is drafted with AI coding tools and reviewed by the engineer before merge, and pull requests that touch the security surface also get the automated security review in § 6.3. These checks catch a lot. They are not the same as an independent human reviewer, and we don't claim they are.
  • Roadmap. A non-author human reviewer for sensitive-area changes (§ 6.3), and then for every non-emergency change, once there is a second engineer. (Roadmap.)

6.3 Sensitive-area reviews

Changes to any of the following get extra care before merge. With one engineer, the area's owner is the author, so there is no separate owner sign-off today. Instead, the automated security review (.github/workflows/security-review.yml) is set to run on pull requests that touch the security surface it lists, which covers these areas except the fly*.toml files that hold release_command, and the author reads its findings before merging. A non-author owner acknowledgment is Roadmap (§ 6.2). The areas are:

  • The encryption module (st-backend/app/core/encryption.py).
  • The Row-Level Security policies (st-backend/app/db/rls.sql) and the tenancy module (st-backend/app/core/tenancy.py).
  • Authentication flows (st-backend/app/api/routes/auth.py, the Clerk webhook handler).
  • Audit-log writes (the audit_service).
  • Deploy workflows and release_command configuration.

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