Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,3 @@ DJANGO_SECRET_KEY=replace-with-a-local-development-only-value
DJANGO_DEBUG=true
ALLOWED_HOSTS=localhost,127.0.0.1
DATABASE_URL=postgresql://skillstreak:change-me-for-local-development@db:5432/skillstreak

# Fixed local MVP sign-in. Choose both values in your untracked .env file.
# Do not commit the real values or use this demo sign-in in production.
DEMO_LOGIN_EMAIL=demo@example.com
DEMO_LOGIN_PASSWORD=choose-a-password
20 changes: 20 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ jobs:
quality:
name: Python quality and infrastructure harness
runs-on: ubuntu-24.04
services:
postgres:
image: postgres:17.7-bookworm
env:
POSTGRES_DB: skillstreak
POSTGRES_USER: skillstreak
POSTGRES_PASSWORD: ci-test-password
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U skillstreak -d skillstreak"
--health-interval 5s
--health-timeout 3s
--health-retries 12
env:
DATABASE_URL: postgresql://skillstreak:ci-test-password@localhost:5432/skillstreak
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
Expand All @@ -29,6 +45,10 @@ jobs:
run: pytest tests/infrastructure
- name: Django checks when application bootstrap exists
if: ${{ hashFiles('manage.py') != '' }}
env:
DJANGO_SETTINGS_MODULE: skillstreak.settings.production
DJANGO_SECRET_KEY: ci-only-deployment-check-signing-value-not-used-outside-continuous-integration-2026
ALLOWED_HOSTS: localhost
run: python manage.py check --deploy
- name: Django tests and coverage when application bootstrap exists
if: ${{ hashFiles('manage.py') != '' }}
Expand Down
31 changes: 17 additions & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@

## Status and source of truth

This repository is at the infrastructure-foundation stage. Read
This repository is at the first-account-slice stage. Read
`infrastructure_plan.md` before changing tooling, Docker, CI, dependency, or
delivery decisions. The currently implemented configuration follows that plan:
Python 3.14, Django 5.2 LTS, pip-tools, local PostgreSQL in Compose, and planned
Google Cloud Run delivery through Artifact Registry. The first product slice is
a local, fixed-demo sign-in and visual dashboard; database-backed product
behavior does not exist yet.
internal `@csuchico.edu` account registration and a visual dashboard;
user-owned skill/progress behavior does not exist yet.

## Repository map

- `infrastructure_plan.md`: approved infrastructure plan.
- `requirements.in`, `requirements.txt`, `pyproject.toml`, `.python-version`: Python toolchain and lockfile.
- `requirements.in`, `requirements.txt`, `requirements.runtime.in`,
`requirements.runtime.txt`, `pyproject.toml`, `.python-version`: Python
toolchain and development/runtime lockfiles.
- `compose.yml`, `Dockerfile`, `.dockerignore`, `.env.example`: Docker infrastructure and safe local configuration template.
- `scripts/`: infrastructure validation, container-image, and PostgreSQL smoke tests.
- `tests/infrastructure/`: configuration harness tests only.
- `.github/workflows/`: `pr-checks.yml` and guarded `release.yml`.
- `.agents/skills/`: repository-provided skills.
- `src/dashboard/`: Django fixed-demo sign-in and dashboard; it has no product
data persistence.
- `docs/specs/dashboard-visual-prototype.md`: approved MVP sign-in scope.
- `docs/decisions/001-fixed-demo-sign-in.md`: rationale for the temporary
sign-in design.
- Database-backed product apps, API endpoints, migration files, real accounts,
and browser tests: **not created yet**.
- `src/accounts/`: email-only internal team accounts and first user migration.
- `src/dashboard/`: authenticated static dashboard; it has no user-owned
product data persistence.
- `docs/specs/internal-team-accounts.md`: approved account scope.
- `docs/decisions/002-internal-email-accounts.md`: account-authentication
rationale.
- Database-backed skills/progress apps, API endpoints, and browser tests:
**not created yet**.

## Required reading and skill routing

Expand All @@ -42,9 +45,9 @@ apply.

## Boundaries

- During infrastructure work, do not create Django apps, pages, routes, models,
domain schemas, authentication flows, business logic, product fixtures, or
production data.
- During infrastructure work, do not create unrelated Django apps, pages,
routes, models, domain schemas, authentication flows, business logic, product
fixtures, or production data.
- Do not change a plan decision without updating `infrastructure_plan.md` through
the planning workflow.
- Never commit secrets, `.env` files, credentials, generated reports, local
Expand Down
11 changes: 8 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONDONTWRITEBYTECODE=1
WORKDIR /build

COPY requirements.txt ./
RUN python -m pip install --prefix=/install --requirement requirements.txt
COPY requirements.runtime.txt ./
RUN python -m pip install --prefix=/install --requirement requirements.runtime.txt

FROM python:3.14.7-slim-bookworm AS runtime

Expand All @@ -17,7 +17,12 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
PATH=/usr/local/bin:$PATH
WORKDIR /app

RUN groupadd --gid 10001 app && useradd --uid 10001 --gid app --create-home app
RUN apt-get update \
&& apt-get upgrade --yes \
&& apt-get install --yes --no-install-recommends libpq5 \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --gid 10001 app \
&& useradd --uid 10001 --gid app --create-home app
COPY --from=builder /install /usr/local
COPY --chown=app:app . .
RUN chown app:app /app
Expand Down
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# SkillStreak

SkillStreak is planned as a public, multi-account Django web application with
PostgreSQL. This repository currently contains its development, quality, Docker,
delivery foundation, a minimal Django source framework, liveness route, and a
local fixed-demo dashboard sign-in at `/`. No database-backed product schema,
real account system, API, or production deployment exists yet.
SkillStreak is planned as a multi-account Django web application with PostgreSQL.
This repository currently contains its development, quality, Docker, delivery
foundation, a minimal Django source framework, liveness route, internal
`@csuchico.edu` account registration/sign-in, and a static sample dashboard at
`/`. No user-owned skill/progress schema, API, or production deployment exists
yet.

## Repository map

Expand All @@ -17,8 +18,8 @@ real account system, API, or production deployment exists yet.
| `tests/application/`, `tests/infrastructure/` | Django framework and infrastructure tests | Ready |
| `docs/specs/`, `docs/plans/` | Reviewed specifications and implementation plans | Bootstrap work documented |
| `.github/workflows/` | Pull-request checks and guarded Cloud Run release workflow | Ready |
| `src/`, `manage.py` | Django project framework, liveness endpoint, and fixed-demo dashboard | Ready; no database-backed product behavior |
| `docs/specs/dashboard-visual-prototype.md` | Approved fixed-demo MVP scope | Current |
| `src/`, `manage.py` | Django framework, liveness endpoint, team accounts, and sample dashboard | Ready; no user-owned product behavior |
| `docs/specs/internal-team-accounts.md` | Approved internal-account scope | Current |
| `.agents/skills/` | Repository-specific agent guidance | Available |

## Getting Started
Expand All @@ -32,11 +33,12 @@ real account system, API, or production deployment exists yet.

Never commit `.env`. Cloud Run receives real `DATABASE_URL` and
`DJANGO_SECRET_KEY` values from Google Cloud-managed secrets.
3. Regenerate the dependency lockfile after editing `requirements.in`:
3. Regenerate the dependency lockfiles after editing `requirements.in` or the
production-only `requirements.runtime.in`:

```sh
docker run --rm --volume "$PWD:/workspace" --workdir /workspace python:3.14.7-slim-bookworm \
sh -c 'python -m pip install "pip-tools>=7.5,<8" && python -m piptools compile --strip-extras --output-file requirements.txt requirements.in'
sh -c 'python -m pip install "pip-tools>=7.5,<8" && python -m piptools compile --strip-extras --output-file requirements.txt requirements.in && python -m piptools compile --strip-extras --output-file requirements.runtime.txt requirements.runtime.in'
```
4. Start the local Django and PostgreSQL services:

Expand All @@ -45,11 +47,15 @@ real account system, API, or production deployment exists yet.
```

The application listens on `http://localhost:8000` by default. Set
`APP_PORT` in `.env` to choose another host port. The root page provides a
fixed-demo sign-in. Set `DEMO_LOGIN_EMAIL` and `DEMO_LOGIN_PASSWORD` in your
untracked `.env` file before starting it; only that pair can access the
sample dashboard. This temporary sign-in is for local MVP validation, not
production accounts. Verify its liveness endpoint from another terminal:
`APP_PORT` in `.env` to choose another host port. Run migrations before
registering an internal `@csuchico.edu` account and accessing the sample
dashboard:

```sh
docker compose exec web python manage.py migrate
```

Verify its liveness endpoint from another terminal:

```sh
curl --fail http://localhost:8000/healthz
Expand Down Expand Up @@ -87,8 +93,8 @@ migrations and browser workflows remain future product work.

## Quality and CI

`requirements.txt` is the committed pip-tools lockfile. Pull requests verify the
lockfile, Ruff formatting and linting, the infrastructure harness, Gitleaks,
`requirements.txt` and `requirements.runtime.txt` are committed pip-tools
lockfiles. Pull requests verify the development lockfile, Ruff formatting and linting, the infrastructure harness, Gitleaks,
CodeQL, and an image build with a critical-vulnerability scan. Django system
checks and the 80% branch-and-line coverage gate now run in pull requests.
Browser tests remain future product work.
Expand Down
2 changes: 0 additions & 2 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ services:
DJANGO_DEBUG: ${DJANGO_DEBUG:-true}
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-replace-with-a-local-development-only-value}
DJANGO_SETTINGS_MODULE: skillstreak.settings.local
DEMO_LOGIN_EMAIL: ${DEMO_LOGIN_EMAIL:-}
DEMO_LOGIN_PASSWORD: ${DEMO_LOGIN_PASSWORD:-}
networks: [application]
ports:
- "${APP_PORT:-8000}:8000"
Expand Down
2 changes: 1 addition & 1 deletion docs/decisions/001-fixed-demo-sign-in.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Status

Accepted — 2026-09-30
Superseded by [ADR-002](002-internal-email-accounts.md) — 2026-10-05

## Context

Expand Down
38 changes: 38 additions & 0 deletions docs/decisions/002-internal-email-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ADR-002: Use email-only internal team accounts

## Status

Accepted — 2026-10-05

Supersedes [ADR-001](001-fixed-demo-sign-in.md).

## Context

The fixed demo credential was appropriate for the first visual prototype but
cannot provide separate, durable accounts for the project team. The team needs
self-service registration and normal returning-user sign-in without adding an
outbound email provider or public-account lifecycle.

## Decision

Use a custom Django user model from the first project migration. Its normalized
`@csuchico.edu` email address is the unique login identifier; there is no
username. Store Django authentication sessions in PostgreSQL, retain the
existing fourteen-day session age, and use Django's built-in password validators
and password hashing.

Registration creates a usable account immediately. It verifies the domain only,
not mailbox ownership. Sign-in, duplicate registration, and redirect behavior
avoid disclosure beyond generic failure messages and the dashboard always
redirects unauthenticated visitors to the sign-in page.

## Consequences

- The custom user model must remain configured before the first applied shared
migration; a database already migrated with `auth.User` requires a separately
reviewed migration project.
- No email verification means email addresses are not trusted recovery or
notification channels. Password reset/change, email change, invitations, and
account deletion are intentionally deferred.
- The demo environment credentials and signed-cookie authorization marker are
removed. Existing demo cookies cannot authorize the dashboard.
3 changes: 3 additions & 0 deletions docs/plans/dashboard-visual-prototype-implementation.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Implementation Plan: Dashboard Visual Prototype

> Historical — authentication portions superseded by
> [Internal Team Accounts](../specs/internal-team-accounts.md).

## Overview

Replace the client-only dashboard preview with an approved, fixed-demo
Expand Down
19 changes: 10 additions & 9 deletions docs/plans/proposed-schema-erd.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ metrics at all, one daily metric, or several repeated measurements.

```mermaid
erDiagram
AUTH_USER ||--o| USER_PREFERENCE : has
AUTH_USER ||--o{ SKILL : creates
ACCOUNTS_USER ||--o| USER_PREFERENCE : has
ACCOUNTS_USER ||--o{ SKILL : creates
SKILL o|--o{ SKILL : categorizes
SKILL ||--o{ METRIC_DEFINITION : declares
AUTH_USER ||--o{ USER_SKILL : tracks
ACCOUNTS_USER ||--o{ USER_SKILL : tracks
SKILL ||--o{ USER_SKILL : is_selected_for
USER_SKILL ||--o{ DAILY_COMPLETION : has
USER_SKILL ||--o{ USER_SKILL_SCHEDULE : configures
Expand All @@ -24,9 +24,9 @@ erDiagram
DAILY_COMPLETION ||--o{ METRIC_MEASUREMENT : contains
METRIC_DEFINITION ||--o{ METRIC_MEASUREMENT : describes

AUTH_USER {
ACCOUNTS_USER {
bigint id PK
string username
string email UK
}

USER_PREFERENCE {
Expand Down Expand Up @@ -111,11 +111,12 @@ erDiagram

## Entities and rules

### `AUTH_USER`
### `ACCOUNTS_USER`

The Django-configured user model. Django models must refer to it through
`settings.AUTH_USER_MODEL`, never by a hard-coded table name. The entity is
shown here only to make ownership clear.
The email-only Django-configured user model. Its normalized `@csuchico.edu`
email address is unique and is the authentication identifier. Django models
must refer to it through `settings.AUTH_USER_MODEL`, never by a hard-coded
table name. The entity is shown here only to make ownership clear.

### `USER_PREFERENCE`

Expand Down
43 changes: 20 additions & 23 deletions docs/specs/dashboard-visual-prototype.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Status

Approved — the MVP uses a fixed, environment-configured demo sign-in with a
server-side session. It is not an account-registration or password-management
feature.
Superseded for authentication by
[Internal Team Accounts](internal-team-accounts.md). The static dashboard
visual scope remains current.

## Objective

Expand All @@ -15,10 +15,7 @@ responsive dashboard with illustrative skill data and streak history.
## Scope

- A server-rendered home page at `/`.
- A POST-only sign-in form that accepts exactly one fixed demo identity from
`DEMO_LOGIN_EMAIL` and `DEMO_LOGIN_PASSWORD` environment variables.
- A signed, HTTP-only Django session that persists the verified state across
page loads for up to 14 days, or until the visitor logs out.
- An authenticated project-team session supplied by the internal-account flow.
- A desktop sidebar, dashboard header, skill summaries, and calendar-like
streak-history display inspired by the wireframe.
- A predefined, filler skill library represented by static example entries.
Expand All @@ -27,8 +24,8 @@ responsive dashboard with illustrative skill data and streak history.

## Out of Scope

- Django accounts, registration, password recovery, user models, migrations,
database reads/writes, and API endpoints.
- Password recovery, user-owned dashboard data, database-backed skills, and API
endpoints.
- Skill detail, add-skill, social, friends, chat, settings, and messages pages.
- Any claim that the illustrative progress data belongs to a real user.

Expand All @@ -44,30 +41,30 @@ docker run --rm --volume "$PWD:/app" --workdir /app skillstreak-dashboard-test r
## Structure

`dashboard` is a small Django app responsible for the server-rendered MVP.
Its template and CSS are colocated in the app. It compares form values against
runtime configuration with a constant-time comparison and stores only a
boolean demo-session marker in Django's signed-cookie session backend.
Its template and CSS are colocated in the app. Access is controlled by Django
authentication; illustrative dashboard values remain static example entries.

## Testing Strategy

Focused Django client tests verify successful sign-in, failed sign-in, session
persistence, logout, and dashboard content. Browser testing will verify the
desktop interaction and layout when a browser-control service is available.
Focused Django client tests verify authenticated dashboard access, logout, and
dashboard content. Account-flow tests cover registration and sign-in. Browser
testing will verify the desktop interaction and layout when a browser-control
service is available.

## Boundaries

- Always: use CSRF-protected POST forms, generic failure messages, signed
- Always: use CSRF-protected POST forms, generic failure messages, secure
HTTP-only SameSite cookies, semantic HTML, and keyboard-operable controls.
- Ask first: add real accounts, database persistence, dependencies, or a
production demo-login policy.
- Never: commit the demo credential, log submitted values, or expose whether
either login field was individually correct.
- Ask first: add user-owned data, password recovery, email verification, or
outbound email.
- Never: log submitted passwords or expose whether either sign-in field was
individually correct.

## Success Criteria

- `/` returns a sign-in prompt when no valid demo session exists.
- `/` redirects to sign-in when no authenticated session exists.
- Invalid sign-in attempts remain blocked with one generic error message.
- A successful sign-in reaches the dashboard and remains signed in for up to
14 days; a POST logout clears the session immediately.
- An authenticated account reaches the dashboard and remains signed in for up
to 14 days; a POST logout clears the session immediately.
- The dashboard presents filler Jogging and Reading streak history on desktop;
narrow layouts remain usable.
Loading
Loading