From 9d4c7d2b4163ec05eac5eda5fc676335a877a4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 17 May 2026 20:38:07 +0200 Subject: [PATCH 1/3] Archive staging-environments change; update local-dev spec Staging environments are live and fully operational. Marks the remaining verification tasks complete, syncs the delta spec to main specs, and archives the change. Also adds a "When to Use Local vs. Staging" reference table to the local-dev-environment spec so the boundary between the two environments is documented in one place. Co-Authored-By: Claude Sonnet 4.6 --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/infrastructure/spec.md | 0 .../specs/staging-environment/spec.md | 0 .../2026-05-17-staging-environments}/tasks.md | 6 +- openspec/specs/local-dev-environment/spec.md | 12 ++++ openspec/specs/staging-environment/spec.md | 55 +++++++++++++++++++ 8 files changed, 70 insertions(+), 3 deletions(-) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/.openspec.yaml (100%) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/design.md (100%) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/proposal.md (100%) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/specs/infrastructure/spec.md (100%) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/specs/staging-environment/spec.md (100%) rename openspec/changes/{staging-environments => archive/2026-05-17-staging-environments}/tasks.md (93%) create mode 100644 openspec/specs/staging-environment/spec.md diff --git a/openspec/changes/staging-environments/.openspec.yaml b/openspec/changes/archive/2026-05-17-staging-environments/.openspec.yaml similarity index 100% rename from openspec/changes/staging-environments/.openspec.yaml rename to openspec/changes/archive/2026-05-17-staging-environments/.openspec.yaml diff --git a/openspec/changes/staging-environments/design.md b/openspec/changes/archive/2026-05-17-staging-environments/design.md similarity index 100% rename from openspec/changes/staging-environments/design.md rename to openspec/changes/archive/2026-05-17-staging-environments/design.md diff --git a/openspec/changes/staging-environments/proposal.md b/openspec/changes/archive/2026-05-17-staging-environments/proposal.md similarity index 100% rename from openspec/changes/staging-environments/proposal.md rename to openspec/changes/archive/2026-05-17-staging-environments/proposal.md diff --git a/openspec/changes/staging-environments/specs/infrastructure/spec.md b/openspec/changes/archive/2026-05-17-staging-environments/specs/infrastructure/spec.md similarity index 100% rename from openspec/changes/staging-environments/specs/infrastructure/spec.md rename to openspec/changes/archive/2026-05-17-staging-environments/specs/infrastructure/spec.md diff --git a/openspec/changes/staging-environments/specs/staging-environment/spec.md b/openspec/changes/archive/2026-05-17-staging-environments/specs/staging-environment/spec.md similarity index 100% rename from openspec/changes/staging-environments/specs/staging-environment/spec.md rename to openspec/changes/archive/2026-05-17-staging-environments/specs/staging-environment/spec.md diff --git a/openspec/changes/staging-environments/tasks.md b/openspec/changes/archive/2026-05-17-staging-environments/tasks.md similarity index 93% rename from openspec/changes/staging-environments/tasks.md rename to openspec/changes/archive/2026-05-17-staging-environments/tasks.md index e46ade3..887b095 100644 --- a/openspec/changes/staging-environments/tasks.md +++ b/openspec/changes/archive/2026-05-17-staging-environments/tasks.md @@ -8,7 +8,7 @@ - [x] 2.1 Create `infrastructure/docker-compose.staging.yml` with staging journal (port 3100), planner (port 3101), memory limits (256MB), and `trails_staging` database URL - [x] 2.2 Create `infrastructure/staging.env.template` documenting required staging environment variables (DOMAIN, DATABASE_URL, JWT_SECRET, SESSION_SECRET) - [x] 2.3 Add a shared Docker network (`trails-shared`) to production `docker-compose.yml` so staging can reach BRouter and PostgreSQL -- [ ] 2.4 Verify staging containers start with `docker compose -f docker-compose.staging.yml -p trails-staging up -d` on the server +- [x] 2.4 Verify staging containers start with `docker compose -f docker-compose.staging.yml -p trails-staging up -d` on the server ## 3. Caddy Wildcard Routing @@ -16,7 +16,7 @@ - [x] 3.2 Add `planner.staging.trails.cool` site block proxying to planner on port 3101 - [x] 3.3 Add `import sites/*.caddyfile` to the main Caddyfile so per-PR site blocks can be dropped in and picked up on reload - [x] 3.4 Define the per-PR Caddyfile snippet template the cd-staging workflow writes for each preview (host = `pr-.staging.trails.cool`, upstream = `host.docker.internal:`, port = `3200 + 2N`) -- [ ] 3.5 Reload Caddy and verify staging routes work with `curl -sf https://staging.trails.cool/api/health` +- [x] 3.5 Reload Caddy and verify staging routes work with `curl -sf https://staging.trails.cool/api/health` ## 4. GitHub Actions Workflow @@ -30,7 +30,7 @@ - [x] 5.1 Create a scheduled cleanup job (weekly cron in GitHub Actions or pg-boss on the server) that lists running `trails-pr-*` containers, checks PR status via `gh pr view`, and tears down orphans - [x] 5.2 Add memory limits (`deploy.resources.limits.memory: 256m`) to staging containers in the compose override -- [ ] 5.3 Test full lifecycle: open a test PR → verify preview deploys → push a commit → verify preview updates → close PR → verify teardown +- [x] 5.3 Test full lifecycle: open a test PR → verify preview deploys → push a commit → verify preview updates → close PR → verify teardown ## 6. Documentation diff --git a/openspec/specs/local-dev-environment/spec.md b/openspec/specs/local-dev-environment/spec.md index e9de720..53853c0 100644 --- a/openspec/specs/local-dev-environment/spec.md +++ b/openspec/specs/local-dev-environment/spec.md @@ -2,6 +2,18 @@ One-command local development setup with PostgreSQL, BRouter, automatic schema migration, and segment downloading. +## When to Use Local vs. Staging + +| Scenario | Use | +|----------|-----| +| Everyday development, unit tests, E2E tests | Local (`pnpm dev:full`) | +| Sharing a work-in-progress with reviewers | PR preview (`pr-.staging.trails.cool`) | +| Testing against production-like infra (Caddy TLS, Docker networking) | Staging (`staging.trails.cool`) | +| Wahoo OAuth callback testing | Local with `HTTPS=1` (see CLAUDE.md) | +| ActivityPub federation testing | Staging (requires real HTTPS + public domain) | + +The Planner, Journal, PostgreSQL, and BRouter all run locally. The local stack is the default for all development work. Staging is not a replacement for local dev — it is a pre-merge verification surface and a way to share work without merging first. + ## Requirements ### Requirement: One-command dev startup diff --git a/openspec/specs/staging-environment/spec.md b/openspec/specs/staging-environment/spec.md new file mode 100644 index 0000000..5d9541e --- /dev/null +++ b/openspec/specs/staging-environment/spec.md @@ -0,0 +1,55 @@ +## Requirements + +### Requirement: Persistent staging instance +A persistent staging instance SHALL run at `staging.trails.cool` (journal) and `planner.staging.trails.cool` (planner), auto-deploying from main on every push. + +#### Scenario: Deploy staging on main push +- **WHEN** a commit is pushed to main that changes `apps/` or `packages/` +- **THEN** the staging journal and planner containers are rebuilt and redeployed with the latest main + +#### Scenario: Staging uses isolated database +- **WHEN** the staging instance is running +- **THEN** it uses the `trails_staging` database, separate from the production `trails` database + +#### Scenario: Staging is accessible +- **WHEN** a user navigates to `https://staging.trails.cool` +- **THEN** they see the journal app served over HTTPS with a valid TLS certificate + +### Requirement: PR preview environments +Ephemeral preview environments SHALL be created for each PR that changes app or package code. + +#### Scenario: Preview created on PR open +- **WHEN** a PR is opened that changes files in `apps/` or `packages/` +- **THEN** a preview environment is deployed at `pr-.staging.trails.cool` within 5 minutes +- **AND** a comment is posted on the PR with the preview URL + +#### Scenario: Preview updated on PR push +- **WHEN** new commits are pushed to a PR branch with an active preview +- **THEN** the preview containers are rebuilt and redeployed with the latest branch code + +#### Scenario: Preview torn down on PR close +- **WHEN** a PR is merged or closed +- **THEN** its preview containers are stopped and removed +- **AND** its database (`trails_pr_`) is dropped + +#### Scenario: Preview database isolation +- **WHEN** a PR preview is running +- **THEN** it uses a dedicated database `trails_pr_` with schema applied via Drizzle Kit push + +### Requirement: PR preview cleanup +A scheduled cleanup job SHALL remove orphaned preview resources from closed PRs. + +#### Scenario: Stale preview cleanup +- **WHEN** the cleanup job runs +- **THEN** any preview containers or databases belonging to closed/merged PRs are removed + +### Requirement: Resource limits +Staging and preview containers SHALL have resource limits to protect production. + +#### Scenario: Memory limits enforced +- **WHEN** a staging or preview container is running +- **THEN** it has a memory limit of 256MB per app container + +#### Scenario: Concurrent preview limit +- **WHEN** more than 3 PR previews are active +- **THEN** the oldest preview is torn down before the new one is created From 0e77ac7831bac0308d1dbdd71c78b49600f137c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 17 May 2026 20:41:03 +0200 Subject: [PATCH 2/3] Update local-dev-stack proposal: staging now exists Co-Authored-By: Claude Sonnet 4.6 --- openspec/changes/local-dev-stack/proposal.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openspec/changes/local-dev-stack/proposal.md b/openspec/changes/local-dev-stack/proposal.md index 14a66a0..1aac619 100644 --- a/openspec/changes/local-dev-stack/proposal.md +++ b/openspec/changes/local-dev-stack/proposal.md @@ -1,12 +1,12 @@ ## Why -There is no staging environment — production is the only deployed instance. Infra -changes (Prometheus alerts, Caddy config, Grafana dashboards) go straight to prod -with no way to validate them locally first. Meanwhile, CI E2E tests skip ~15 of -20 tests because there is no PostgreSQL service in the workflow, and BRouter was -only recently added to CI with manual setup instead of reusing the dev compose -file. The existing `docker-compose.dev.yml` covers PostgreSQL and BRouter but -nothing else from the production stack. +Infra changes (Prometheus alerts, Caddy config, Grafana dashboards) can only be +validated against staging or production — there is no way to test them locally +first. Meanwhile, CI E2E tests skip ~15 of 20 tests because there is no +PostgreSQL service in the workflow, and BRouter was only recently added to CI +with manual setup instead of reusing the dev compose file. The existing +`docker-compose.dev.yml` covers PostgreSQL and BRouter but nothing else from the +production stack. ## What Changes From 61b787ca84f9bd08b2d956ec677dec1f062b8247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 17 May 2026 20:43:00 +0200 Subject: [PATCH 3/3] Fix staging-environment spec: add missing Purpose section Co-Authored-By: Claude Sonnet 4.6 --- openspec/specs/staging-environment/spec.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openspec/specs/staging-environment/spec.md b/openspec/specs/staging-environment/spec.md index 5d9541e..7cfd58e 100644 --- a/openspec/specs/staging-environment/spec.md +++ b/openspec/specs/staging-environment/spec.md @@ -1,3 +1,7 @@ +## Purpose + +Production-like staging and ephemeral PR preview environments running alongside production on the flagship server, enabling integration testing before merge and live URL sharing with reviewers. + ## Requirements ### Requirement: Persistent staging instance