Team Task Manager (TTM) is a backend-first Django SaaS project for team workspaces, projects, tasks, comments, and audit activity.
The project is intentionally built around service-oriented domain logic, selector-based reads, and centralized permission helpers instead of fat models, views, or serializers.
TTM models a workspace-driven collaboration flow:
- users join workspaces through memberships and invitations
- workspaces contain projects
- projects contain tasks
- tasks support assignment, status updates, priorities, due dates, and comments
- important mutations are recorded in an activity log
The HTML interface is intentionally minimal and server-rendered. It exists as a lightweight functional surface for the backend, not as a frontend-heavy product.
The UI is intentionally lightweight, but the project includes a polished server-rendered surface for demo and manual QA flows.
- workspace creation and membership-based access control
- invitation creation, acceptance, and revocation
- role management for
owner,admin, andmember - explicit workspace ownership transfer
- project archiving and unarchiving
- read-only enforcement for archived projects
- task creation, editing, assignment, and status changes
- comment soft delete with permission boundaries
- activity log for key workspace events
- JWT-authenticated REST API with Swagger/OpenAPI docs
- health and readiness endpoints for deployment
- local Codex automation through management commands and MCP tools
- Python 3.13
- Django 5.1
- Django REST Framework
- Simple JWT
- PostgreSQL via
DATABASE_URL - SQLite fallback for local development when
DATABASE_URLis unset - WhiteNoise for static files
- Render-ready deployment config
TTM follows a strict domain architecture:
serviceshandle state changes and business workflowsselectorshandle read/query use casescore.permissionscontains centralized authorization rules- views, forms, and serializers remain thin
- multi-step mutations use
transaction.atomic()
accounts: profile model and authentication pagesworkspaces: workspaces, memberships, invitations, ownership workflowsprojects: projects inside workspaces, archive lifecycletasks: tasks, assignment, status changes, task maintenance workflowscomments: task comments with soft deleteactivity: append-only workspace activity logapi: DRF endpoints and JWT authcore: shared permissions, slugs, exceptions, health checks, agent automation, MCP server
team_task_manager/
|-- .github/
|-- accounts/
|-- activity/
|-- api/
|-- comments/
|-- core/
|-- docs/
|-- projects/
|-- tasks/
|-- team_task_manager/
|-- templates/
`-- workspaces/
Important files:
team_task_manager/settings.py: Django settings,DATABASE_URL, DRF, static handlingteam_task_manager/urls.py: HTML, API, docs, health, and readiness routescore/permissions.py: centralized permission checkscore/slugs.py: immutable slug generation helperscore/health.py: readiness and liveness checkscore/agent.py: local automation workflows for Codex-style agentscore/mcp_server.py: native MCP server for Codexworkspaces/services.py: invitation, membership, and ownership workflowsprojects/services.py: project create/archive/unarchive workflowstasks/services.py: task creation, update, assignment, status, and archived-project guardscomments/services.py: comment create/delete workflows with soft delete behavioractivity/services.py: append-only activity writerapi/serializers.py: thin serializers delegating writes to servicesapi/views.py: DRF endpoints reusing the same domain logic as HTML flows
- write logic lives in app services such as
workspaces/services.py,projects/services.py,tasks/services.py, andcomments/services.py - read logic lives in selectors such as
workspaces/selectors.py,projects/selectors.py,tasks/selectors.py,comments/selectors.py, andactivity/selectors.py - permission rules live in
core/permissions.py - HTML views and DRF serializers call those layers instead of implementing business logic inline
TTM is PostgreSQL-first in local development. SQLite remains available as a secondary fallback for quick demos and agent smoke tests when DATABASE_URL is intentionally unset.
Recommended Windows flow:
- Copy environment settings:
copy .env.example .env- Set
DATABASE_URLto your local PostgreSQL instance. - Bootstrap the local environment:
bootstrap_ttm_local.cmd- Seed demo data when you want a ready-to-browse workspace:
seed_ttm_demo.cmd- Start the local server:
start_ttm_local.cmdManual flow:
- Create and activate a virtual environment.
- Install dependencies:
pip install -r requirements.txt- Copy environment settings:
copy .env.example .env-
Update
.envvalues as needed. -
Run migrations:
python manage.py migrate- Create a superuser:
python manage.py createsuperuser- Start the local server:
python manage.py runserverThe project reads .env automatically. If DATABASE_URL is omitted, Django falls back to local SQLite at db.sqlite3. That path is supported for quick local use, but PostgreSQL is the main development target.
TTM reads the main database connection from DATABASE_URL.
Example local PostgreSQL value:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/team_task_manager
DB_SSL_REQUIRE=FalseIf DATABASE_URL is not set, local commands and Codex automation use:
sqlite:///db.sqlite3
That fallback is convenient for development, but production should always use PostgreSQL.
Windows-friendly helper scripts in the repository root:
bootstrap_ttm_local.cmd: rebuild.venv, install dependencies, and run migrationsstart_ttm_local.cmd: run the site on127.0.0.1:8000test_ttm_local.cmd: run the Django test suitelint_ttm_local.cmd: runruffcoverage_ttm_local.cmd: run the full test suite with coverage and enforce the local thresholdseed_ttm_demo.cmd: create or refresh deterministic demo datacheck_ttm_integrity.cmd: run domain integrity checks and return non-zero on failures
bootstrap_ttm_local.cmd prefers py -3.13 when it is healthy and falls back to the bundled Codex Python runtime when the Windows launcher is unavailable or broken.
Useful variants:
seed_ttm_demo.cmd --reset: rebuild demo users and workspace from scratch
The repository includes render.yaml and build.sh for Render deployment.
Render behavior:
- installs dependencies in
build.sh - collects static files during build
- runs migrations in
preDeployCommand - starts Gunicorn with a Uvicorn worker
- uses
/healthz/for health checks - exposes
/readyz/for deeper readiness validation - auto-configures secure proxy and HTTPS-related settings when
RENDERis present
Blueprint flow:
- Push the repository with
render.yaml. - Create a new Blueprint in Render.
- Render provisions the web service and PostgreSQL database.
- Create an admin user from the Render shell:
python manage.py createsuperuserManual Render values:
- Build Command:
./build.sh - Pre-Deploy Command:
python manage.py migrate --no-input - Start Command:
python -m gunicorn team_task_manager.asgi:application -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:$PORT
The repository also includes a production-oriented Docker setup:
Dockerfiledocker-compose.ymldocker/entrypoint.sh
Typical startup flow:
python manage.py migrate --noinputpython manage.py collectstatic --noinputgunicornon port8000
Example:
docker compose up --build -d
docker compose logs -f web
docker compose downKey server-rendered routes:
//healthz//readyz//accounts/signup//accounts/login//workspaces//workspaces/create//workspaces/<slug>//workspaces/<slug>/members//workspaces/<slug>/members/<membership_id>/role//workspaces/<slug>/members/<membership_id>/remove//workspaces/<slug>/invitations/<invitation_id>/revoke//workspaces/<slug>/transfer-ownership//workspaces/<slug>/activity//invitations/<token>/accept//workspaces/<workspace_slug>/projects//workspaces/<workspace_slug>/projects/<project_slug>//workspaces/<workspace_slug>/projects/<project_slug>/archive//workspaces/<workspace_slug>/projects/<project_slug>/unarchive//workspaces/<workspace_slug>/projects/<project_slug>/tasks//workspaces/<workspace_slug>/projects/<project_slug>/tasks/<task_slug>//workspaces/<workspace_slug>/projects/<project_slug>/tasks/<task_slug>/edit/
POST /api/auth/token/POST /api/auth/token/refresh/
GET, POST /api/workspaces/GET /api/workspaces/<slug>/GET /api/workspaces/<slug>/activity/GET, POST /api/workspaces/<slug>/invitations/DELETE /api/workspaces/<slug>/invitations/<invitation_id>/GET, PATCH, DELETE /api/workspaces/<slug>/memberships/<membership_id>/POST /api/workspaces/<slug>/transfer-ownership/POST /api/invitations/<token>/accept/
GET, POST /api/projects/GET /api/workspaces/<workspace_slug>/projects/<project_slug>/POST /api/workspaces/<workspace_slug>/projects/<project_slug>/archive/POST /api/workspaces/<workspace_slug>/projects/<project_slug>/unarchive/
GET, POST /api/tasks/POST /api/tasks/bulk-update/GET, PATCH /api/workspaces/<workspace_slug>/projects/<project_slug>/tasks/<task_slug>/GET, POST /api/comments/DELETE /api/comments/<id>/
GET /api/activity/GET /api/workspaces/<slug>/activity/
/api/projects/?workspace=<workspace-slug>/api/projects/?is_archived=true/api/projects/?created_by=<user-id>/api/projects/?q=analytics/api/projects/?ordering=created_at/api/tasks/?project=<project-slug>/api/tasks/?workspace=<workspace-slug>/api/tasks/?status=todo/api/tasks/?priority=high/api/tasks/?assignee=<user-id>/api/tasks/?created_by=<user-id>/api/tasks/?due_before=2026-06-30/api/tasks/?due_after=2026-06-01/api/tasks/?is_overdue=true/api/tasks/?q=release/api/tasks/?ordering=-created_at/api/comments/?task=<task-slug>/api/comments/?author=<user-id>/api/comments/?is_deleted=false/api/comments/?q=note/api/activity/?workspace=<workspace-slug>/api/activity/?actor=<user-id>/api/activity/?action=task_status_changed/api/activity/?target_type=task/api/activity/?ordering=-created_at
- Swagger UI:
/api/docs/ - OpenAPI schema:
/api/schema/
Example:
curl -X POST http://127.0.0.1:8000/api/auth/token/ \
-H "Content-Type: application/json" \
-d "{\"username\":\"owner\",\"password\":\"secret123\"}"Bulk task maintenance example:
curl -X POST http://127.0.0.1:8000/api/tasks/bulk-update/ \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d "{
\"workspace_slug\": \"engineering\",
\"project_slug\": \"backend\",
\"task_slugs\": [\"ship-api\", \"write-docs\"],
\"status\": \"done\",
\"assignee_id\": 3
}"TTM exposes a local command-driven automation layer for Codex-style agents.
These commands call Django services directly, so they reuse the same permissions, slug rules, and activity logging as the HTML and API layers.
Useful commands:
python manage.py agent_list_workspaces --actor owner
python manage.py agent_list_projects --actor owner --workspace engineering
python manage.py agent_list_members --actor owner --workspace engineering
python manage.py agent_list_tasks --actor owner --workspace engineering --project backend
python manage.py agent_create_project --actor owner --workspace engineering --name "Ops Console"
python manage.py agent_create_task --actor owner --workspace engineering --project backend --title "Add audit export"
python manage.py agent_update_task --actor owner --workspace engineering --project backend --task ship-api --status doneHigher-level request capture:
python manage.py agent_capture_request --actor owner --request "action: create_task
workspace: Engineering
project: Backend
title: Add audit export
description: Build a command for exporting workspace activity
priority: high
assignee: alice"Preview before writing:
python manage.py agent_capture_request --actor owner --preview --request "action: create_task
workspace: Engineering
project: Backend
title: Preview task only"Batch requests are supported with --- separators, and file-based workflows are available through agent_apply_file.
Markdown checklists also work for bulk task creation and maintenance, including Task Action: update_task.
Structured requests can also use Russian keys and values.
The repo also exposes a native MCP server at core/mcp_server.py.
That server wraps the same Django domain services used by HTML, API, and management commands, so Codex can operate on TTM through local tools instead of browser automation.
Available MCP tools:
ttm_get_contextttm_list_workspacesttm_list_projectsttm_list_membersttm_list_tasksttm_create_projectttm_create_taskttm_update_taskttm_close_taskttm_apply_requestttm_apply_file
The MCP server supports TTM_AGENT_DEFAULT_ACTOR, which makes repeated Codex-driven operations simpler in local workflows.
Full test suite:
python manage.py testUseful checks:
python manage.py check
python manage.py makemigrations --check --dry-run
python -m ruff check .Coverage:
coverage run --source=accounts,activity,api,comments,core,projects,tasks,workspaces manage.py test
coverage report --show-missing --fail-under=85Operational endpoints:
GET /healthz/: liveness probe for the Django processGET /readyz/: readiness probe that verifies database access and unapplied migrations
Operational commands:
python manage.py seed_demo_datapython manage.py seed_demo_data --resetpython manage.py check_domain_integrity
GitHub Actions runs on pushes to master and on pull requests.
The workflow:
- installs dependencies on Python 3.13
- runs a dedicated
lintjob - runs a dedicated
django-checkjob with migration drift checks - runs PostgreSQL-backed tests
- enforces
85%total coverage and publishes a coverage artifact from a separate coverage job




