Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
quality:
name: quality
runs-on: ubuntu-latest
timeout-minutes: 20
env:
ASTRO_TELEMETRY_DISABLED: "1"
steps:
- name: Check out source
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Use pinned Node.js
uses: actions/setup-node@v7
with:
node-version-file: .node-version
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Type-check
run: npm run check
- name: Unit tests
run: npm test
- name: Production build
run: npm run build
59 changes: 59 additions & 0 deletions .github/workflows/post-deploy-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Post-deploy smoke

on:
workflow_run:
workflows: [CI]
types: [completed]

permissions:
contents: read

jobs:
sentinel:
name: production sentinel
if: >-
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.head_repository.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Refuse an unverified main deployment
if: github.event.workflow_run.conclusion != 'success'
run: |
echo "CI quality did not succeed for this main commit. Pages may still have attempted deployment."
exit 1
- name: Confirm verified main CI
if: github.event.workflow_run.conclusion == 'success'
run: echo "CI quality succeeded for this main commit."
smoke:
name: production smoke
needs: sentinel
if: >-
needs.sentinel.result == 'success' &&
github.event.workflow_run.conclusion == 'success'
concurrency:
group: production-smoke
cancel-in-progress: true
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out deployed commit
uses: actions/checkout@v6
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
- name: Use pinned Node.js
uses: actions/setup-node@v7
with:
node-version-file: .node-version
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Wait for deployed SHA and smoke production
run: >-
npm run smoke --
--environment production
--base-url https://ratemyplace.org
--expected-release ${{ github.event.workflow_run.head_sha }}
--wait-for-release-ms 600000
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ playwright/.auth/
# Claude Code worktrees
.claude/worktrees/

# Agent implementation worktrees
.worktrees/

# Google Drive sync artifacts
.tmp.driveupload/

Expand Down
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.16.0
36 changes: 24 additions & 12 deletions .planning/PROJECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,29 @@ Tenants can submit honest, anonymous reviews and see aggregated scores for build
- ✓ Cross-view data consistency E2E (search ↔ detail ↔ profile) — v1.5.0
- ✓ Standardized rate-limit response headers (Retry-After, X-RateLimit-*) — v1.5.0
- ✓ Shared EmptyState component for consistent messaging — v1.5.0
- ✓ Landlord and property-manager aggregate scores withheld until three approved reviews, from one shared threshold constant — post-v1.5.0

### Active

- [ ] Component refactors (3 files >700 LOC: ReviewEditForm, BuildingsTable, ReviewsTable)
- [ ] Convert admin dispute-upheld email to fireAndForget (`disputes/[id].ts` follow-up from v1.5.0)
- [ ] Apply `isValidEmail` primitive to signup.ts (consistency follow-up)
- [ ] Email unsubscribe management before scaling notification emails
- [x] Review and approve the v1.6.0 "Trust + Density" design — approved 2026-08-27.
- [ ] Decompose approved v1.6 phases into reviewable implementation plans.
- [ ] Execute trust controls before the bounded review-density pilot.

### Preserved carry-over backlog

- [ ] Convert the admin dispute-upheld email path to `fireAndForget`.
- [ ] Apply the shared `isValidEmail` primitive to signup while Phase 25 touches that flow.
- [ ] Specify unsubscribe/opt-out behavior before any new non-transactional notification-email program.
- [ ] Revisit the large-component refactors after v1.6 unless a touched phase makes a smaller extraction necessary.

### Out of Scope

- Multi-language support — deferred to v2.0
- Delayed posting — deferred
- Landlord response features (direct rebuttals on reviews) — explicitly excluded from MVP
- Real-time push notifications — Cloudflare Workers stateless; polling sufficient
- Email unsubscribe management — track in v1.5.0 before scaling notification emails
- New non-transactional notification-email programs until opt-out and unsubscribe behavior is separately specified
- Component refactors unrelated to a touched v1.6 surface
- Stress testing — deferred from v1.3.0, lower priority than user-facing features

## Latest Shipped: v1.5.0 "Closed Loops" (2026-04-29)
Expand All @@ -70,23 +78,27 @@ Tenants can submit honest, anonymous reviews and see aggregated scores for build

## Next Milestone

**v1.6.0** — Planning to be initiated via `/gsd:new-milestone`. Carry-over candidates: component refactors (>700 LOC files: ReviewEditForm, BuildingsTable, ReviewsTable); admin `disputes/[id].ts` fireAndForget conversion; signup.ts validation consistency; email unsubscribe management.
**v1.6.0 "Trust + Density"** — Approved phases 22–30 close release, privacy, verification-document, contribution, review-integrity, account-rights, accessibility, and discoverability gaps before a bounded review-density pilot. Apartment/unit numbers remain optional private moderation data entered by the reviewer, owner-editable/exportable while the review is owned, and visible to authorized admins, never on public surfaces. Account deletion removes the user link but retains reviews, their private unit numbers, and their score contribution as permanently ownerless records.

- Roadmap: `.planning/milestones/v1.6.0-ROADMAP.md`
- Requirements: `.planning/milestones/v1.6.0-REQUIREMENTS.md`
- Approved design: `docs/superpowers/specs/2026-08-26-trust-density-design.md`

## Context

- **Tech stack**: Astro 5 + Cloudflare Pages + D1 (SQLite) + Lucia Auth + Tailwind CSS 4 + Resend
- **Current version**: v1.5.0 "Closed Loops" (shipped 2026-04-29)
- **Production URL**: ratemyplace.org
- **Codebase**: ~28,000 LOC (TypeScript/TSX/Astro), 322+ unit tests, 18 test files
- **Database tables**: 14 (users, sessions, reviews, buildings, landlords, property_managers, email_verification_tokens, rate_limits, disputes, audit_logs, contact_messages, notifications, saved_buildings, bug_reports)
- **Migrations**: 24 (most recent: 0024_perf_indexes.sql for hot-path index)
- **Codebase**: Strict TypeScript/TSX/Astro with Vitest unit tests and Playwright E2E tests
- **Database**: Cloudflare D1; inspect the current schema rather than relying on a copied table count
- **Migrations**: Source-controlled through 0028; production 0025–0027 were dashboard-applied and 0028 was executed remotely outside Wrangler migration tracking, so ledger reconciliation is required before any new remote migration
- **Admin pages**: Dashboard, Users, Reviews, Buildings, Landlords, Managers, Verification, Disputes, Audit Log, Contact
- **Survey items**: 32 total — 27 scored rating items (Unit 10 + Building 9 + Landlord 8, OHQS/PHQS-adapted) + 5 ancillary items (would_recommend, tenure_months, move_out_year, accepts_housing_vouchers, safely_lit_at_night)
- **Runtime typing**: All Cloudflare Pages secrets declared in `App.Platform.env`; zero `(context.locals as any).runtime` casts in `src/`

## Constraints

- **Platform**: Cloudflare Workers (no Node.js APIs, React 18 only)
- **Platform**: Cloudflare Pages SSR on the Workers runtime (no Node.js APIs, React 18 only), plus a separately deployed scheduled Worker where v1.6 requires Cron Triggers
- **Email**: Resend (selected and integrated)
- **Database**: D1 (SQLite) — single-region, no transactions across requests

Expand All @@ -100,7 +112,7 @@ Tenants can submit honest, anonymous reviews and see aggregated scores for build
| Web Crypto API for tokens | Cross-environment compatibility (Workers + Node.js) | ✓ Good |
| 64-char alphanumeric tokens | 381 bits entropy, URL-safe | ✓ Good |
| Graceful email failure | Signup succeeds even if email fails | ✓ Good |
| Best-effort audit logging | Audit failures don't break admin actions | ✓ Good |
| Atomic destructive-action audit logging | Current helper is best-effort; v1.6 makes every destructive admin D1 mutation, required audit row, and durable external-cleanup intent one batch, while remote cleanup remains non-blocking | ⚠ Planned; closes a non-negotiable audit gap |
| UNIQUE constraint on dispute review_id | One dispute per review, enforced at DB level | ✓ Good |
| Structured JSON logging | Machine-parseable logs for Cloudflare dashboard | ✓ Good |
| CityAdapter pattern for enrichment | Extensible multi-city support without modifying dispatcher | ✓ Good (v1.4.0) |
Expand All @@ -114,4 +126,4 @@ Tenants can submit honest, anonymous reviews and see aggregated scores for build
| EmptyState .astro + .tsx byte-identical twins | Same DOM from SSR and React-island consumers | ✓ Good (v1.5.0) |

---
*Last updated: 2026-04-29 after v1.5.0 "Closed Loops" milestone*
*Last updated: 2026-08-27 after v1.6.0 "Trust + Density" design approval*
31 changes: 30 additions & 1 deletion .planning/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- ✅ **v1.3.0 Battle Tested** — Phases 4-9 (shipped 2026-03-10)
- ✅ **v1.4.0 Open Doors** — Phases 10-15 (shipped 2026-03-22)
- ✅ **v1.5.0 Closed Loops** — Phases 16-21 (shipped 2026-04-29)
- 📝 **v1.6.0 Trust + Density** — Phases 22-30 (design approved; implementation planning)

## Phases

Expand Down Expand Up @@ -74,6 +75,25 @@ See: `.planning/milestones/v1.5.0-ROADMAP.md`

</details>

<details open>
<summary>📝 v1.6.0 Trust + Density (Phases 22-30) — PLANNED</summary>

- [ ] Phase 22: Release, Operations, and Migration Safety
- [ ] Phase 23: Privacy Contract and Data Minimization
- [ ] Phase 24: Verification-Document Lifecycle
- [ ] Phase 25: Contribution Continuity and Trust Copy
- [ ] Phase 26: Review Integrity and Moderation Priority
- [ ] Phase 27: Account Rights and Erasure Recovery
- [ ] Phase 28: Accessible Contribution
- [ ] Phase 29: Discoverability Baseline
- [ ] Phase 30: Review-Density Pilot

- Roadmap: `.planning/milestones/v1.6.0-ROADMAP.md`
- Requirements: `.planning/milestones/v1.6.0-REQUIREMENTS.md`
- Design: `docs/superpowers/specs/2026-08-26-trust-density-design.md`

</details>

## Progress

| Phase | Milestone | Plans Complete | Status | Completed |
Expand All @@ -99,6 +119,15 @@ See: `.planning/milestones/v1.5.0-ROADMAP.md`
| 19. D1 Index Migration | v1.5.0 | 2/2 | Complete | 2026-04-29 |
| 20. Critical-Flow E2E Coverage | v1.5.0 | 2/2 | Complete | 2026-04-29 |
| 21. Quality Cleanup | v1.5.0 | 2/2 | Complete | 2026-04-29 |
| 22. Release, Operations, and Migration Safety | v1.6.0 | — | Planned | — |
| 23. Privacy Contract and Data Minimization | v1.6.0 | — | Planned | — |
| 24. Verification-Document Lifecycle | v1.6.0 | — | Planned | — |
| 25. Contribution Continuity and Trust Copy | v1.6.0 | — | Planned | — |
| 26. Review Integrity and Moderation Priority | v1.6.0 | — | Planned | — |
| 27. Account Rights and Erasure Recovery | v1.6.0 | — | Planned | — |
| 28. Accessible Contribution | v1.6.0 | — | Planned | — |
| 29. Discoverability Baseline | v1.6.0 | — | Planned | — |
| 30. Review-Density Pilot | v1.6.0 | — | Planned | — |

---
*Roadmap updated: 2026-04-29 — v1.5.0 "Closed Loops" milestone complete*
*Roadmap updated: 2026-08-27 — v1.6.0 "Trust + Density" design approved; implementation planning in progress*
16 changes: 14 additions & 2 deletions .planning/codebase/INTEGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,20 @@
- Custom domain: `ratemyplace.org` (DNS CNAME to Cloudflare)

**CI Pipeline:**
- Not detected - No GitHub Actions, GitLab CI, or other CI service configured
- Local npm scripts for build/test: `npm run build`, `npm test`, `npm run e2e`
- **Workflow 1 — CI** (`.github/workflows/ci.yml`): runs for pull requests and pushes to
`main`. Its stable check name is **`quality`** and it runs `npm ci`, `npm run check`,
`npm test`, and `npm run build` with read-only repository permissions.
- **Workflow 2 — Post-deploy smoke** (`.github/workflows/post-deploy-smoke.yml`): every
qualifying internal `main` CI completion runs a non-cancellable `sentinel` job. It
explicitly fails red when `quality` did not succeed and explicitly passes on success;
it never checks out, installs dependencies, or runs smoke. The separate, success-only
`smoke` job needs that sentinel and alone owns cancellable `production-smoke`
concurrency. It then waits for Cloudflare Pages to serve the exact commit SHA and runs
the read-only production smoke suite.
- The repository workflows do not deploy or roll back Cloudflare Pages. A `main` branch
ruleset/required-check activation is not asserted here; Task 7 must verify that external
configuration separately.
- Local checks use `npm ci`, `npm run check`, `npm test`, and `npm run build`.

## Environment Configuration

Expand Down
2 changes: 1 addition & 1 deletion .planning/codebase/STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ ratemyplace-boston/
- Public: `index.astro` (home), `search.astro`, `[slug].astro` (building detail), `profile.astro`, `about.astro`, `methodology.astro`
- Auth: `auth/signin.astro`, `signup.astro`, `forgot-password.astro`
- Admin: `admin/index.astro`, `admin/reviews.astro`, `admin/buildings.astro`, `admin/landlords.astro`, `admin/users.astro`, `admin/disputes.astro`, `admin/audit.astro`
- API: 47 endpoints total (auth, reviews, admin CRUD, disputes, contact, bug reports)
- API: Auth, reviews, admin CRUD, disputes, contact, bug reports, and read-only release health

**`src/components/`**
- Purpose: Reusable React islands and Astro static components
Expand Down
Loading
Loading