尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Source code for policeconduct.org
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Astro static site with AWS infrastructure managed by Terraform.
PoliceConduct.org helps residents evaluate whether law-enforcement agencies and the people working there appear safe, accountable, transparent, and improving. The site also aims to identify positive deviance: officers, supervisors, units, agencies, policies, and practices that produce unusually good public-trust and public-safety outcomes under comparable constraints. Positive examples must be evidence-backed and framed as practices to investigate or learn from, not as blanket endorsements.
./scripts/bootstrap-dev.sh
npm run doctor
npm run
npm ci
npm run dev
Set FORMS_API_PROXY_TARGET to the preview forms API URL in .env-policeconduct
for local UI testing against deployed forms infrastructure.
npm run is the project command surface. Common setup and validation commands:
npm run setup: interactive developer bootstrap.npm run doctor: read-only readiness check for local tools and agent workflow files.npm run openspec:status -- <change-name>: show OpenSpec change status.npm run validate:openspec: validate all OpenSpec artifacts.npm run validate: run the aggregate validation gate.package.json defines the executable gates. Keep this checklist and the PR
template synchronized whenever those scripts change. All items below apply to
the final PR head, including documentation-only PRs.
mise exec -- node --version and mise exec -- npm --version to record
the configured runtime (mise.toml, currently Node 24). Install with
mise exec -- npm ci; use the nested lockfile for any nested package.
Compare every installed package version to its package-lock.json entry
(including transitive packages); do not borrow shared node_modules.git rev-parse HEAD, then run mise exec -- npm run validate in
full. Preserve complete output and command exit status. Re-run the entire
command after fixes or any head change. Scoped checks do not replace it.| Aggregate stage | Current scripts / required evidence |
|---|---|
| Repository formatting | format:check: repository-wide Prettier |
| JavaScript and SQL lint | lint → lint:es (ESLint), lint:sql (pinned SQLFluff through mise) |
| Astro/type diagnostics | validate:types: errors, warnings, and individually reviewed hints |
| Shell syntax | validate:shell: hooks and deployment/bootstrap scripts |
| Database schema contract | validate:schema: configured local database and table outcome |
| OpenSpec | validate:openspec: all changes/specs, including warning output |
| Personal-data scan | scan:personal-data: tracked tree, finding count |
| Redirect tests | test:redirects: coverage, router, loading, map generation, release guard |
| Media tests | test:media: video and personnel photos |
| Forms API tests | test:lambda: nested locked dependencies and Lambda suite |
| CSS tests | test:css: built CSS purging |
| Personal-data tests | test:personal-data: scanner regressions |
| Coverage tests | test:coverage: coverage calculations |
| Browser tests | test:e2e: all Playwright suites, including built-index search |
validate:no-inline-css checks appropriate to the
change. Record commands and outcomes. A full final build and sitemap
verification are production-release gates, not PR merge prerequisites.This repo uses Codex App, OpenSpec, Superpowers, and the OpenSpec Superpowers
bridge for behavior-changing work. OpenSpec captures the intended behavior;
Superpowers supplies the development discipline; the bridge keeps brainstorming,
plans, verification, and retrospectives in openspec/changes/<change-name>/.
Recommended flow:
npm run doctor
npm run validate:openspec
npx openspec schemas
npx openspec status --change <change-name>
Then brainstorm the outcome, reuse the active branch and worktree (creating
another requires explicit human permission), and ask Codex to turn the brainstorm into an
OpenSpec change. Use /opsx:continue <change-name> until proposal/spec/task/plan
artifacts are coherent, then /opsx:apply <change-name>, /opsx:verify <change-name>, /opsx:continue <change-name> for the retrospective, and
/opsx:archive <change-name>.
Direct PR-sized changes are acceptable for documentation-only edits, formatting, tooling tweaks, and internal refactors that preserve specified behavior.
Build requires RECAPTCHA_SITE_KEY. The build script loads .env,
.env-recaptcha, and .env-policeconduct before validating env.
npm run build
The build pipeline:
validate-build-env.mjs)GIT_COMMIT_SHA and GIT_COMMIT_DIRTY from git stateCSS is externalized (inlineStylesheets: "never") and tree-shaken via PurgeCSS
so that Bootstrap and custom styles ship as a single shared file instead of being
inlined into every page.
Build metadata is derived from git state automatically:
GIT_COMMIT_SHA defaults to the current git commit.GIT_COMMIT_DIRTY defaults to 1 when tracked or untracked changes exist.The current layout does not emit these values as page meta tags. Record the build's commit and clean/dirty state in the verification evidence; compare served artifacts with that build rather than assuming a page exposes its source SHA.
Production deploys also attach build metadata to each uploaded S3 object as
build-commit and build-dirty. build.json is not part of the standard
build, audit, preview deploy, or production deploy flow.
Audits run against the existing dist/ output. dist/ is not checked in, and
audits require a fresh full build first. Stale or partial build output is not a
supported audit baseline.
npm run build
npm run audit
Run either audit directly when needed:
npm run audit:seo
npm run audit:a11y
Optional tuning for large sites:
| Variable | Default | Description |
|---|---|---|
SEO_AUDIT_MAX_PAGES | 5000 | HTML files checked by seo:audit |
A11Y_MAX_URLS | 200 | URLs sampled from sitemap for headless a11y |
A11Y_PORT | 4173 | Local audit server port |
Required search coverage needs generated assets. With the intended local database, intake fixtures, environment, and Playwright browsers configured, generate the HTML needed by the tests and its Pagefind index, then serve on loopback before the full validation gate. A full build is one way to prepare those assets:
mise exec -- npm run build
mise exec -- npm run preview -- --host 127.0.0.1 --port 4321
# In another terminal with the same configured environment:
mise exec -- npm run validate
The browser runner reuses the server on port 4321. Confirm it serves this head's
matching generated HTML and index, not an unrelated or stale process. Existing
assets may be reused when their source matches the final head (for example,
a documentation-only change). npm run build:search-index generates the index
from rendered dist/ HTML. Required browser assertions must actually run;
preparing their assets does not make the complete build or sitemap a merge gate. Without a built server the runner
starts the development server, which cannot satisfy required Pagefind coverage;
missing search assets fail instead of skipping. Scoped npm run test:e2e -- ...
runs remain useful development feedback only.
These tests mock reCAPTCHA and /api/forms/* responses to keep runs stable and
independent from external services.
Preview deploy builds the site with a PR-specific SITE_URL, syncs dist/ to
the preview S3 prefix, and invalidates the preview CloudFront path for that PR.
export PR_NUMBER=<pr-number>
export S3_BUCKET_PREVIEW=<preview-bucket>
export CLOUDFRONT_DIST_PREVIEW=<preview-distribution-id>
npm run deploy:preview
Before production release, require an error-free full final build and sitemap verification for the release artifact. Record these separately from PR merge evidence; merging a PR does not satisfy or waive either release check.
Production deploy uses the incremental deploy script. It runs a fresh build by
default, uploads only changed files from dist/, deletes removed objects, and
invalidates only the affected CloudFront paths.
export S3_BUCKET=<prod-bucket>
export CLOUDFRONT_DIST_ID=<prod-distribution-id>
npm run deploy:prod
To deploy an existing dist/ without rebuilding:
export S3_BUCKET=<prod-bucket>
export CLOUDFRONT_DIST_ID=<prod-distribution-id>
npm run deploy:prod -- --skip-build
If .deploy-cache/d is empty and you already synced the current production site
locally, seed the incremental baseline manifest before the first run:
bash scripts/seed-deploy-manifest.sh /path/to/synced-site
Then compare your current dist/ against that baseline:
export S3_BUCKET=<prod-bucket>
export CLOUDFRONT_DIST_ID=<prod-distribution-id>
bash scripts/deploy-incremental.sh --skip-build --dry-run
After the baseline exists, npm run deploy:prod and
bash scripts/deploy-incremental.sh use the same production deploy flow.
Required for production:
| Variable | Description |
|---|---|
S3_BUCKET | Production S3 bucket |
CLOUDFRONT_DIST_ID | Production CloudFront distribution |
Required for preview:
| Variable | Description |
|---|---|
PR_NUMBER | Pull request number used in the preview URL and S3 prefix |
S3_BUCKET_PREVIEW | Preview S3 bucket |
CLOUDFRONT_DIST_PREVIEW | Preview CloudFront distribution |
Optional:
| Variable | Description |
|---|---|
GIT_COMMIT_SHA | Override commit hash (default: auto-detected from git) |
GIT_COMMIT_DIRTY | Override dirty flag (default: auto-detected from git) |
Terraform bootstrap stack in infrastructure/bootstrap-policeconduct/.
Provisions:
POST /forms/draft, POST /forms/submit, GET /status/{submissionId})Bootstrap usage:
cp infrastructure/bootstrap-policeconduct/terraform.tfvars.example \
infrastructure/bootstrap-policeconduct/terraform.tfvars
export GITHUB_TOKEN=<github-token-with-repo-admin-access>
terraform -chdir=infrastructure/bootstrap-policeconduct init
bash infrastructure/bootstrap-policeconduct/scripts/apply.sh
See infrastructure/README.md and infrastructure/bootstrap-policeconduct/README.md.
After running:
bash infrastructure/bootstrap-recaptcha/scripts/apply.shbash infrastructure/bootstrap-policeconduct/scripts/apply.shyour .env-recaptcha and .env-policeconduct should include the canonical
runtime/build variables below.
.env-policeconduct is the assembled runtime file for site/forms local runs.
bootstrap-policeconduct sync merges Terraform outputs and required reCAPTCHA
values (fallback from .env-recaptcha) and fails if required keys are missing.
DRAFTS_BUCKETDRAFTS_KMS_KEY_IDSUBMISSIONS_BUCKETSUBMISSIONS_KMS_KEY_IDRECAPTCHA_PROJECT_IDRECAPTCHA_SITE_KEYRECAPTCHA_SERVICE_ACCOUNT_EMAILRECAPTCHA_WIF_PROVIDER_RESOURCE_NAMERECAPTCHA_WIF_AUDIENCERECAPTCHA_SITE_KEY/status//api/status/{submissionId}200 with JSON.
If no status file exists, response falls back to:
{ "submissionId": "<id>", "status": "pending" }.TF_VAR env names)RECAPTCHA_PROJECT_IDRECAPTCHA_SITE_KEYRECAPTCHA_SERVICE_ACCOUNT_EMAILRECAPTCHA_WIF_PROVIDER_RESOURCE_NAMERECAPTCHA_WIF_AUDIENCEOptional examples:
GA_MEASUREMENT_ID_PRODUCTIONGA_MEASUREMENT_ID_PREVIEWSENTRY_DSN_PRODUCTIONSENTRY_DSN_PREVIEWSENTRY_ORGSENTRY_PROJECTSENTRY_AUTH_TOKENTF_STATE_BUCKETAWS_ROLE_ARNS3_BUCKETS3_BUCKET_PREVIEWCLOUDFRONT_DIST_IDCLOUDFRONT_DIST_PREVIEWDRAFTS_BUCKETSUBMISSIONS_BUCKETTF_OUT_* keys are generated automatically for every Terraform output.Frontend Sentry is enabled when these are set:
PUBLIC_SENTRY_DSNPUBLIC_SENTRY_ENVIRONMENTTerraform bootstrap can manage GitHub environment vars/secrets for the same org and project:
TF_VAR_sentry_org (set to your Sentry org slug)TF_VAR_sentry_project (set to PoliceConduct)TF_VAR_sentry_auth_token (for source-map upload during deploys)https://8cantwait.org/ https://clean.calmatters.org https://clearinghouse.net https://endqi.org/ https://ij.org/report/policing-for-profit-4\ https://invisible.institute https://legalaidnyc.org/law-enforcement-look-up https://mappingpoliceviolence.org/ https://mappingpoliceviolence.us https://nixthe6.org/ https://openoversight.com https://policecrime.bgsu.edu https://policedata.org https://policescorecard.org/ https://policeviolencereport.org/ https://safecitiesproject.org/ (sans economic info) https://www.minneapolismn.gov/government/government-data/datasource/officer-payouts-dashboard/ https://www.policefilesny.com