Week-to-week roster operations for coordinators, volunteers, players, and staff
- Greedy Heuristic Solver — auto-generate fair schedules with role-based constraints
- Responsive web app — full admin + volunteer workflow in the browser, served by the same FastAPI process (walkthrough below) — the primary surface
- Flutter mobile app (
mobile/) — volunteer + admin app, local analysis and tests; seemobile/README.mdfor status - CLI + API — schedule from YAML files or through REST endpoints
- Multi-tenant — full org isolation with JWT auth and RBAC (admin/volunteer)
- Invitation system — token-based volunteer onboarding
- Browser request integrity — signed CSRF tokens and exact-origin checks protect form and HTMX writes
- Truthful responses — unanswered, accepted, declined, and replacement-needed work stay distinct from roster allocation
- Availability tracking — volunteers block dates, time-off with reasons
- Calendar export — ICS files and webcal subscriptions
SignUpFlow is an open-source application you run yourself. No hosted service, paid plan, or production deployment is included. Billing and paid SMS stay disabled by default and are not required for the Church or Basketball workflows. Production-like startup is fail-closed for signing keys, database/origin settings, release identity, test bypasses, and enabled-provider coherence; see the configuration contract. This is a configuration guard, not deployment or provider acceptance.
This walkthrough goes from an empty machine to a published schedule you can see in the browser. Every command below was run against a clean checkout.
| Requirement | Notes |
|---|---|
| Python 3.11, 3.12 or 3.13 | make setup rejects anything outside this range, including 3.14 |
| Poetry | make setup stops immediately if poetry is missing |
make |
Preinstalled on macOS and Linux; on Windows use WSL |
Docker is not required for this walkthrough. If you would rather use it, see Running on Docker below and then rejoin at Step 3.
git clone https://github.com/tomqwu/SignUpFlow.git
cd SignUpFlow
make doctor # reports what this machine will actually start the app with
make setupmake doctor is worth the five seconds, and it runs before make setup on
purpose: it uses only the Python standard library, so it works on a bare clone
with nothing installed. That matters because it is what you run when setup
itself fails.
The app reads its configuration from the environment, so a variable exported in
your shell changes how it starts and a fresh clone cannot clear it. The report
names every value that applies, says whether it came from your shell or from
.env, and exits non-zero on anything that will stop the app from starting. On
a clean machine it prints:
(nothing set; every default applies)
No blocking problems found.
make setup prepares everything the app needs in order to run: dependencies,
any backing services, and the database schema. It does not start the app. It
ends with ✅ Setup complete!. You do not need a .env file; the defaults are
SQLite with every external provider disabled, and nothing is containerised.
Two commands cover the whole lifecycle: make setup prepares the environment
and make up runs the app. What either one does is decided by DATABASE_URL,
not by which command you type. Leave it unset or on SQLite and both stay
entirely on the host. Point it at the compose database and setup brings up
PostgreSQL and Redis and migrates inside that network, and make up serves the
app from the api container — because a compose hostname is only reachable from
within that network. The configuration and the commands cannot disagree.
make upThis serves on http://localhost:8000 with auto-reload, and keeps running until
you press Ctrl+C. Leave it running and use a second terminal for anything else.
make run and make dev are aliases, and make serve forces the host path.
To confirm it is alive:
curl http://localhost:8000/health
# {"status":"healthy","service":"signupflow-api","version":"1.0.0"}make setup finishes by loading a demo organization, Grace Community Church
(demo), built from the Church playbook the same
way a coordinator would build it. Fourteen volunteers joined by invitation into
four teams, with two scheduling rules. Sunday services and band rehearsals run
from two weeks ago to six weeks ahead, and a published schedule staffs every
slot. The first fortnight is accepted, one musician has declined, the sound
tech has asked for a swap, one volunteer is away, and one invitation is still
pending. Setup prints the logins:
| Account | What you will see | |
|---|---|---|
| Admin | admin@example.com |
Dashboard, assignments, swaps, analytics |
| Volunteer, musician | mia.chen@example.com |
A schedule, booked time off, an open shift to pick up |
| Volunteer, worship leader | grace.park@example.com |
A confirmed schedule and inbox |
| Volunteer, sound | priya.nair@example.com |
A pending swap request |
Every account uses the password DemoPass123!. Open http://localhost:8000
and sign in. make test-stack signs in as these accounts in Chromium, WebKit
and Firefox and checks that every page they open works.
The password is published here, so the demo is for local use only. Loading it
is refused when ENVIRONMENT=production, and every address is on
example.com, which cannot receive mail. The dates are fixed when it loads, so
run make seed-demo RESET=1 to rebuild it with fresh ones. make seed-demo
prints the logins again, and make setup SEED_DEMO=false skips the demo.
To start your own organization instead, click Create a new organization on the sign-in page and fill in the form. That first sign-up creates the organization and its first administrator together, in one step. Everyone after that joins by invitation, so this is the only time you will see that form.
Use a browser rather than curl for this. Browser writes carry a CSRF token, so
a bare curl POST to the form is rejected with 403. The JSON API at
/api/v1/auth/signup is available if you want to script it.
You land on the admin dashboard, which links to a Get started checklist at
/a/onboarding. It lists four things and says you can do them in any order:
- Invite a teammate at
/a/people. Each invitation carries the qualifications that person can serve, such asusherorpoint_guard. Qualifications are not permissions; onlyadminandvolunteerare. - Create an event at
/a/events, giving it the roles it requires and how many of each. - Generate a schedule at
/a/solver. The solver fills every required role it can, spreads work fairly, and reports anything it could not cover. - Share the schedule by publishing it. Nothing is visible to volunteers until you publish, and publication is refused while a required role is unfilled.
The checklist has a Skip for now link if you would rather explore directly.
Once published, each volunteer sees their own shifts at /v/schedule and can
accept or decline. You can watch the whole roster at /a/assignments, and
/a/analytics summarises coverage and workload.
From here, the Church and Basketball walkthroughs show the same cycle run week to week, with screenshots.
Press Ctrl+C in the terminal running make up. Your data lives in roster.db,
so make up picks up where you left off. Delete that file and re-run
make setup to start over.
Docker brings PostgreSQL and Redis rather than SQLite. Point DATABASE_URL at
the compose database and the same two commands apply:
echo 'DATABASE_URL=postgresql://signupflow:dev_password_change_in_production@db:5432/signupflow_dev' >> .env
make setup # starts PostgreSQL and Redis, migrates inside that network
make up # serves the app from the api containerRejoin the walkthrough at Step 3 on http://localhost:8000. PostgreSQL is
published on 5433 and Redis on 6380 by default, chosen so they do not collide
with anything already running locally. POSTGRES_PORT and REDIS_PORT override
them, and a .env copied from .env.example sets them to 5432 and 6379. Use
make logs to follow output and make down to stop.
make doctor reports this configuration as the Docker path rather than a
problem, as long as Docker is running.
The individual steps remain available if you want them: make compose-up
starts the stack unconditionally and make migrate-docker migrates inside it.
Both paths share one trap: a DATABASE_URL whose host is db. That is the
compose service name and it resolves only inside the compose network, so on the
host it cannot be reached. make setup stops and tells you which source the
value came from, because the fix differs.
From your shell. An exported DATABASE_URL survives a fresh clone and
overrides .env, so re-cloning or editing .env changes nothing. Clear it:
unset DATABASE_URLand delete any export DATABASE_URL= line from ~/.bashrc, ~/.zshrc or
whichever profile your shell loads. Check with echo "$DATABASE_URL", which
should print an empty line.
From .env. Set DATABASE_URL=sqlite:///./roster.db, or delete the file;
SQLite is the default and no .env is needed.
To reach a real PostgreSQL server from the host, point at its published port,
such as localhost:5433 for the compose database. To run in containers, use
make compose-up and make migrate-docker and let them set it themselves.
poetry run signupflow --helpRun either maintained six-week YAML workspace without a database or API server:
poetry run signupflow solve examples/church
poetry run signupflow solve examples/basketballCreate a new sample or use the equivalent module command:
poetry run signupflow init /tmp/my-church
poetry run signupflow solve /tmp/my-church --json-output
poetry run python -m api.cli.main solve examples/church --json-outputThe examples are deliberately compact. The complete role-by-role business workflow is the API/browser playbook described below and in examples.
Start an owned local server and run the executable Basketball workflow. It creates
an organization and admin atomically, accepts seven member invitations, creates one
fully staffed event, solves it, and publishes it through canonical /api/v1 routes.
EMAIL_ENABLED=false SMS_ENABLED=false BILLING_ENABLED=false make run
curl http://127.0.0.1:8000/health
poetry run python examples/api_client_example.pyExisting organizations reject public signup. Every later member joins through an
administrator-created invitation. The script accepts loopback endpoints only, uses
synthetic .example identities, and contains no real token or provider credential.
Interactive API docs are at http://127.0.0.1:8000/docs.
In the browser, an administrator can create an invitation from People. When
email delivery is disabled, the result shows a one-time link to copy and share with
the invitee; with local capture or an enabled email backend, the link is sent through
that configured channel instead. For links shared outside the administrator's network,
serve the browser app at an invitee-accessible origin and set FRONTEND_URL or APP_URL
to that same origin. Browser writes from a different private origin are rejected.
Treat invitation links as account-creation secrets.
Pending invitations remain on People after a refresh. An administrator can
copy an active manual link again or cancel an invitation, including an expired
one, before creating a replacement for the same email.
SignUpFlow ships a responsive web app (HTMX + Alpine.js + Jinja2) served by the same FastAPI process - same origin, no separate build or deploy. These are real Playwright captures from the fixture-driven Church and Basketball workflows, not mockups. The complete 44-image phone/desktop set and provenance are in the screenshot guide and manifest.
The Church administrator onboards qualified members, reviews a complete six-week service/rehearsal roster, follows up on unanswered work, exposes a real qualified-cover gap, publishes a holiday service with minimized changes, and rolls the horizon forward. The executable scenario catalog covers CH-01, CH-02, CH-03, CH-04, CH-05, CH-06, CH-07, and CH-08.
| Administrator operations | Member and reserve operations |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
The Basketball manager runs the same operating cycle for games and practices, while players and staff retain role-specific responses and cover. A postponed game resets the affected response, preserves staffing, and moves the same logical calendar entry. The executable scenario catalog covers BB-01, BB-02, BB-03, BB-04, BB-05, BB-06, BB-07, and BB-08.
| Manager operations | Player, staff, and reserve operations |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Reproduce the evidence locally with make capture-screenshots, inspect every resulting
image, then run make validate-screenshots. The capture uses synthetic .example data,
a fixed January 9, 2030 clock, Chromium, an owned temporary database, and no providers.
signupflow init / solve → api/cli/main.py (YAML workspace)
POST /api/v1/solver/solve → api/routers/solver.py (HTTP + DB)
│
▼
api/core/solver/heuristics.py
(GreedyHeuristicSolver)
│
▼
SolveContext → SolutionBundle
(people, events, constraints, holidays)
Backend: FastAPI + SQLAlchemy 2.0 + Pydantic 2.x (Python 3.11 to 3.13)
CLI: YAML workspace in, JSON solution out (api.cli.main)
Database: SQLite (dev) / PostgreSQL (prod)
Auth: JWT (HS256) + bcrypt
/api/v1/auth — atomic organization bootstrap, login, refresh, email check
/api/v1/organizations — authenticated read/update/lifecycle operations
/api/v1/people — CRUD for people, /me profile, deactivate a departing member
/api/v1/teams — CRUD for teams + membership
/api/v1/events — CRUD for events + manual assignments
/api/v1/constraints — CRUD for scheduling constraints
/api/v1/solver — POST /solve to generate schedules
/api/v1/solutions — list/view generated solutions
/api/v1/availability — time-off / blocked dates
/api/v1/conflicts — conflict checking
/api/v1/invitations — create/verify/accept invitation tokens
/api/v1/calendar — ICS export
/api/v1/analytics — volunteer + event stats
/api/v1/password-reset — request/confirm password reset
/api/v1/assignments — member responses, open-shift claims, swaps
/api/v1/audit-logs — tenant-scoped administrative audit history
/api/v1/recurring-series — recurring-event series and occurrence operations
/api/v1/resources — venues and capacity records
/api/v1/holidays — organization holiday records
/api/v1/notifications — in-app notification inbox and reconciliation
/api/v1/billing — disabled by default; deferred commercial surface
Notification routes and the SendGrid callback are registered under /api/v1 but
return 404 by default behind EMAIL_ENABLED=false. Scheduling emails sent through
SendGrid carry organization and notification custom arguments; signed callback events
must match both values and the provider message ID before changing delivery state.
Duplicate event IDs are recorded once, and processing failures return a retryable 503.
Production email enablement also requires SENDGRID_WEBHOOK_PUBLIC_KEY.
Billing routes remain under /api/v1, and SMS routes under /api/sms, but both
return 404 by default behind BILLING_ENABLED=false and SMS_ENABLED=false. Paid
billing and SMS are deferred; the complete scheduling workflow does not require them.
The Stripe callback is mounted at /api/v1/webhooks/stripe behind the billing feature
gate. It fails closed without a signing secret, requires tenant metadata, and records
replay/order/reconciliation state before changing local entitlement. The SMS webhook
paths share the disabled /api/sms router.
If SMS is separately authorized and enabled, Twilio callbacks fail closed unless
their signatures match the exact configured external callback URLs. String person
IDs, same-tenant recipients, and assignment/event/person relationships are checked
before provider or queue work.
Provider-backed checkout requests also retain a local operation key and outcome. An uncertain response is marked for reconciliation and the same request is not sent again. This local behavior is not Stripe/Twilio sandbox acceptance. Pricing, refund policy, provider delivery, and live enablement remain unapproved under issue #270.
Run all review and validation locally. make test-all executes the maintained
unit, API/security, CLI, integration, web, contract, and Playwright tiers; GitHub
Actions is not test or code-review evidence. See Testing.
Delegated agent runs use a finite, versioned work-item contract with guarded Git/GitHub access. See Agent runner for Claude/Gemini commands, local evidence, ownership rules, and failure behavior.
Use the church and basketball operational playbooks for six-week acceptance scenarios, reproducible API/browser tests, and explicit manual release checks.
make test-all # All seven Python tiers, including Playwright
make test-postgres # Opt-in owned PostgreSQL migration/business/race checks
make test-redis # Opt-in owned Redis shared-quota checks
make test-artifact # Opt-in image/private-stack/loopback-TLS checks
make test-security # Opt-in committed-source/image scan and SBOM evidence
make test-docs # Tracked documentation ledger and current local links
make test-performance # Legacy assertions; requires an owned loopback server
make test-load # Bounded source-identified local load validation
make test-mobile # Flutter unit/widget tests
make test-mobile-generated # Generated Dart analysis and tests
make mobile-codegen-check # Deterministic generated Dart client drift check
make capture-screenshots # Regenerate asserted Church/Basketball UI evidence
make validate-screenshots # Reject missing, altered, or stale capturesSee the current testing and merge guide for all tiers,
dependencies and local review/validation evidence requirements.
See the tool safety ledger before running maintenance,
migration, provider, database-inspection, Docker cleanup, or legacy helper commands.
make test-all validates the declared suite inventory
and writes a SHA-bound JSON report plus per-tier JUnit and console logs under
test-artifacts/local-validation/. The report names all opt-in scopes that were not run.
Counts and runtimes belong to dated validation reports, not static overview tables.
The web journey matrix inventories every rendered
route and template and binds each workflow family to named happy-path, error, and
permission evidence. Unit validation rejects new web surfaces until that inventory
is updated. Browser recovery tests also cover actionable HTMX validation, preserved
form input after a network failure, rapid duplicate submission, expired sessions,
long labels at phone width and zoom, keyboard access, and authoritative SSE refresh.
Browser request-integrity tests inventory every unsafe rendered route, reject missing or
forged tokens and foreign origins without mutation, verify configured proxy boundaries,
and exercise a real same-origin profile save in Chromium.
API tests exercise event management, conflicts, availability, profiles, teams, scheduling, organization lifecycle, and authorization. They also cover the day-to-day operations around a live roster: cancelling an event and notifying its assignees, retiring or erasing a departing member, re-solving after a qualification change or a manual override, filling a shift that starts today, and the publish/unpublish/correct/republish recovery chain. The executable API authorization matrix records every mounted operation, the organization cancel/restore/hard-delete actor matrix, and the real-JWT tenant regressions for scheduling routes. The owned PostgreSQL drill also proves representative tenant-child cleanup, retained deletion audit evidence, and foreign-tenant survival. Production acceptance still requires the remaining playbook boundaries.
Both API and CLI suites include real-world scenario tests:
Church ministry — A coordinator runs a six-week worship and ministry roster with multi-role volunteers, absences, simultaneous services, shortages, replacement, regeneration, publication, acceptance, and swaps.
Basketball team — A coach runs a six-week game and practice roster with multi-position players, injuries, simultaneous events, shortages, replacement, regeneration, publication, acceptance, and swaps.
Mid-season roster changes — Cancelling an event notifies everyone who was scheduled for it, so a member is never left holding a shift that no longer exists. The notice carries its own copy of the event title, original time, location, and role, because the event row is gone by the time the message is rendered.
A member who leaves should be retired with POST /api/v1/people/{id}/deactivate
rather than deleted. Deactivating reopens their future live work the same way
removing a qualification does, and keeps their completed history intact. A hard
DELETE still exists for genuine erasure requests, but it cascades through
every assignment the person ever held, including past work on published
rosters.
Roster allocation is not member acceptance. See the assignment response contract for persisted states, revision handling, migration behavior, coordinator queues, and replay protection. Open-shift claims, swap covers, and coordinator roster edits share a serialized allocation transaction contract. It preserves draft history, rechecks qualification and availability after locking, prevents overfill and overlap races, and leaves the prior roster unchanged on failure.
Publishing follows a strict full-horizon schedule contract. The server rejects incomplete, stale, ineligible, overlapping, or narrower replacement rosters before changing member visibility. Generated solutions include every event in their solve scope, including unfilled events; legacy scope-less solutions must be regenerated. Explicitly cancel an event, add the next week, regenerate the remaining horizon, and publish only after every required role is covered.
The machine-readable coverage manifest binds the shared BO journeys, every Church/Basketball qualification, stable scenario IDs, execution tiers, and remaining partial/blocked work. Pytest validates it before playbook collection so a missing required role or scenario cannot silently pass.
The browser playbooks create each organization and first admin through normal signup,
then invite and accept fourteen baseline members plus a qualified replacement through
the UI. They preserve admin/volunteer account access separately from custom
scheduling qualifications such as worship_leader, center, and scorekeeper.
The same admin journey creates all six primary and six rehearsal/practice events through
the multi-role browser form, solves the 84-slot horizon, and independently verifies exact
role coverage, distinct qualified assignees, non-overlap, and balanced interchangeable
loads before publication. API and web safety regressions prove incomplete or stale
candidates cannot replace the live roster.
Every Church and Basketball qualification also runs the member availability page at
360px and 1440px: it records Wednesday time off and recurring Sunday unavailability,
rejects peer edits without mutation, and verifies exclusion across twelve API-seeded
events while retaining a complete qualified roster.
The browser playbooks also distinguish recurrence scope at both widths. An administrator moves one occurrence, cancels a different occurrence, verifies that the remaining event is unchanged, and uses a separately labeled action to delete the entire series. Moving an already accepted published event resets that commitment to unanswered, so the assigned member must review and accept the changed time again.
The rolling-horizon journey starts from an API-seeded, already published six-week precondition with its first primary and secondary sessions completed. At both browser widths, each domain administrator adds week seven through the event form, solves and publishes weeks two through seven through the UI, and verifies the new 84-slot roster. The independent oracle proves that the completed event records and every original future commitment remain; fixture seeding is not counted as a browser action.
The local-mail journey runs for Church and Basketball at both browser widths with all paid providers disabled. It writes real RFC 822 messages to an owned temporary directory, accepts the invitation through its captured link, and drives both the administrator and volunteer through logout/login, self-service password change, and captured single-use reset links. Copied pre-change and pre-reset browser sessions, old passwords, replayed links, and expired API tokens fail safely. The same run delivers assignment, schedule-change, and reminder messages from the published roster, reconciles the member inbox, and opens every notification HTTP link against the owned local server. An executable inventory renders assignment, reminder, update, and cancellation HTML plus localized subjects in English, Spanish, French, Portuguese, Simplified Chinese, and Traditional Chinese. This is local business-flow evidence, not proof of external inbox placement or provider reliability.
The personal-calendar journey also runs for both domains at 360px and 1440px. Draft
work is absent, publication creates one entry, a schedule move updates the same UID in
the member's America/Toronto timezone, and cancellation removes it on refresh. Unit
and real-JWT API regressions cover a DST transition, consistent download/feed scope,
declined assignments, and deliberately mismatched foreign-tenant child rows. This proves
local ICS behavior, not the polling interval or rendering of every third-party client.
The two-organization BO-12 journey starts Church and Basketball together in the same disposable application. Each administrator sees only its own people, and one volunteer for every declared Church and Basketball scheduling qualification signs in at 360px and 1440px. Volunteers stay out of the administrator surface and cannot invite, publish, inspect a foreign person, or mutate a peer's availability. API and browser credentials are bound to the active account's tenant; missing, mismatched, or inactive membership claims fail authentication. This is local application evidence, not deployment or infrastructure certification.
The domain late-cover journey is driven by each playbook's declared roles. Church tests separate sound-operator and children's-leader withdrawals; Basketball tests separate coach and scorekeeper withdrawals. At both browser widths, the coordinator sees the real gap, wrong-role and unavailable reserves cannot see it, and one exact qualified available reserve covers it without changing any other player, ministry, or staff slot.
Domain eligibility changes are fixture-driven too. Church removes a future children's ministry qualification, reopens only affected live work, preserves completed history, and shows the exact gap at both browser widths. Basketball records a point guard's multiweek absence, proves solver exclusion inside the interval, and requires a deliberate member update before the player becomes schedulable again. Safeguarding and medical clearance remain explicit human decisions; the application does not infer either one.
Schedule-change operations are fixture-driven at both browser widths as well. Church adds an usher-only holiday service, minimizes roster changes, compares one added and zero removed commitments, republishes, reminds the new assignee, and preserves an existing acceptance. Basketball postpones a point-guard game, resets the affected acceptance, minimizes roster changes, republishes and reminds, and moves the logical calendar entry under the same UID even when publication creates a new assignment row. Ministry approval and venue/opponent coordination remain human decisions outside the application.
Saved REST scheduling rules support hard assignment caps, hard minimum rest gaps, and a weighted soft cooldown preference. They execute in API solves instead of being stored as inert text. See the validated constraint contract for request shapes, CLI equivalents, built-in overlap/availability behavior, and unsupported policy.
make doctor # Report what this machine will start the app with
make setup # Prepare the environment: deps, services, schema
make up # Start the app on :8000 (follows DATABASE_URL)
make test # Complete local Python suite (same as make test-all)
make test-unit # Python unit tests only
make test-unit-fast # Skip slow bcrypt tests (~7s)
make test-all # All Python tiers, including web + contract + Playwright
make test-postgres # Owned ephemeral PostgreSQL acceptance (requires Docker)
make test-redis # Owned Redis quota, event-bus, and broker acceptance
make test-artifact # Exercise the committed image and owned loopback TLS locally
make test-security # Scan that exact image and committed dependency inputs locally
make test-recovery # Owned encrypted SQLite backup and restored-app acceptance
make test-docs # Validate every tracked documentation disposition and current link
make test-mobile # Flutter tests (requires Flutter SDK)
make test-mobile-generated # Generated Dart analysis and tests
make mobile-codegen-check # Verify generated Dart client matches OpenAPI snapshot
make capture-screenshots # Recreate public Church/Basketball screenshots locally
make validate-screenshots # Verify image, fixture, UI-source, and caption metadata
make migrate # Run Alembic migrationsRun the provider-free local delivery workflow directly with
poetry run pytest tests/e2e/test_local_mail_playbooks.py -v. See the
local mail capture guide for its backend contract.
Single test: poetry run pytest tests/unit/test_events.py::test_create_event -v
Tests run locally. GitHub Actions is not test or code-review evidence.
poetry install installs the locked Playwright Python dependency; before the first browser run, install Chromium with
poetry run playwright install chromium (Linux may also require browser system
dependencies). make test-all runs each tier in a separate process, including
both church and basketball playbooks.
Run make test-postgres for database or migration changes. It creates and removes its
own loopback-only PostgreSQL 16 container and writes versioned JUnit/report evidence;
never substitute a shared or customer database.
Run make test-redis for rate-limit, cross-worker refresh, or notification-broker
changes. It creates an authenticated, loopback-only Redis container with ephemeral
storage; proves two limiter instances share one atomic quota; carries tenant-scoped
events between independent bus clients; and verifies broker outage followed by durable
re-enqueue. Production fails protected requests with a retryable 503 when shared quota
storage is unavailable; development keeps explicit process-local fallbacks. This is
local application evidence, not network DDoS or deployed infrastructure acceptance.
Run make test-artifact for production-image changes after committing the tracked tree.
It retains the SHA-labeled image and report, uses private disposable PostgreSQL/Redis,
runs one migration job before two read-only replicas, and contacts no external provider.
It also terminates HTTPS through an ephemeral self-signed loopback proxy and verifies
secure browser cookies, same-origin writes, security headers, and TLS negotiation. This
does not exercise external ingress, managed TLS, staging, or release approval.
After an owner names and authorizes a disposable staging deployment, run
make test-staging with STAGING_BASE_URL, STAGING_EXPECTED_RELEASE_SHA, and
STAGING_APPROVAL_REFERENCE. The command refuses unapproved or non-HTTPS remote targets,
checks the deployed release header and readiness before writes, runs every discovered
Church/Basketball API playbook with generated credentials, verifies browser cookies and
security headers, and writes a sanitized receipt under test-artifacts/staging-validation/.
It creates synthetic staging tenants and does not deploy, enable providers, or establish
operator alert, backup, rollback, capacity, pilot, or production acceptance by itself.
Then run make test-security. The pinned scanner reads a committed archive and the exact
retained image without Docker-socket access, records advisory database and input hashes,
exercises secret/database failure fixtures, and writes sanitized findings, license
inventory, and source/image CycloneDX documents. The release image uses a digest-pinned
upgraded Alpine base and excludes package managers and build tools. See the
local release security guide. A passing local scan is not a
GitHub status, deployment scan, or independent attestation.
Run make test-recovery for SQLite recovery changes. It creates only fictional data,
uses SQLite's backup API so committed WAL data is included, restores an AES-GCM bundle
to a new owned destination, exercises Church/Basketball auth and state, and writes a
source-bound report under test-artifacts/recovery-drill/. It does not schedule a
backup, retain an encryption key, overwrite a database, or perform a cutover. See the
recovery runbook.
Production /health is dependency-free liveness and /ready is sanitized database
readiness. JSON stdout logs carry the request ID and exact RELEASE_SHA; local bounded
readiness/queue/backup signals do not claim an external operator received an alert.
Run make test-mobile for mobile changes;
set FLUTTER=/path/to/flutter if the SDK is not on your PATH.
No CI checks: formatting, lint, type checks, migrations, code review, unit tests, and E2E tests all run locally. Record commands, results and reviewed head/base SHAs in the PR before merging. GitHub does not independently attest local runs; never fabricate a successful status check. Ollama is not a code-review provider. See the current production roadmap.
my-workspace/
org.yaml # Organization config (org_id, region, defaults)
people.yaml # Volunteers: id, name, roles[]
events.yaml # Events: id, type, start, end, required_roles[]
output/ # Generated by solve command
solution.json # Assignments, metrics, violations
- Fork the repository
- After explicit branch authorization, create it:
git switch -c codex/my-feature - Write tests first (TDD), implement, verify with
make test-unit - Stage only owned files, then commit and push
- Open a Pull Request
MIT License — see LICENSE for details.





















