Imported from borgius/cvs-challenge (
AGENTS.md). Install upstream withnpx skills add borgius/cvs-challenge. Copyright stays with the author.
AGENTS.md
Project overview
PR Concierge is a small TypeScript service for pull request hygiene and release awareness.
It accepts GitHub pull request webhooks, validates the signature, fetches changed files from the GitHub API, applies deterministic checks, scores risk, publishes a pr-concierge GitHub check run for supported PR events, and returns a short summary.
The current application surface is:
GET /healthPOST /webhooks/github
The HTTP layer uses Hono. The local server and AWS Lambda handler both come from src/index.ts, and the main route orchestration lives in src/app.ts.
Read these docs first
Before making architecture or workflow changes, read these files:
README.mdfor the current scaffold, route list, and environment variablesdocs/project-overview.mdfor the intended product and AWS target architecturedocs/build-by-stages.mdanddocs/multiagent-stage-briefs.mdfor planning context
Important: the planning docs are slightly ahead of the implementation. For example, some stage docs still refer to a service/ directory, but the real application code lives in src/. Follow the current code first, then update docs if they drift.
Repository layout
src/app.ts— Hono app, middleware, route handlers, webhook orchestrationsrc/index.ts— local Node server bootstrap and AWS Lambdahandlerexportsrc/config/env.ts— environment variable loading and validationsrc/github/— GitHub signature validation, changed-file API clients, and check publication helperssrc/services/evaluatePullRequest.ts— PR evaluation workflowsrc/risk/classifier.ts— deterministic risk classification rulessrc/storage/evaluationRepository.ts— persistence interface and placeholder repositorysrc/types/— shared domain and GitHub payload typesinfra/bootstrap/tofu-backend/— one-time backend bootstrap root for the OpenTofu S3 bucket and DynamoDB lock tableinfra/terraform/— OpenTofu root and local wrapper modules for the AWS footprintdocs/— product, build-plan, and multi-agent planning notes.github/workflows/— CI and deployment workflowsdist/— generated build output; do not edit by hand
Setup commands
Use npm in this repository. The lockfile is package-lock.json.
- Install dependencies:
npm install - Build the app:
npm run build - Type-check only:
npm run typecheck - Type-check the Vitest harness:
npm run typecheck:tests - Run the default local verification command:
npm run test - Run only the local Lambda integration suite:
npm run test:integration:local - Run the deployed HTTP API integration suite:
npm run test:integration:deployed - Start the local development server:
npm run dev - Start the compiled build:
npm start - Configure this repository's managed
pull_requestwebhook explicitly after deploy:bash scripts/configure-self-webhook.sh
Local development is most reliable on the same major Node version used in CI. The workflows use Node.js 22. The project docs still target AWS Lambda Node.js 20 for deployment, so keep runtime code compatible with Lambda expectations.
Environment setup
Use .env.example as the canonical template. .env is gitignored.
Keep .env focused on local runtime values and the deployment secrets that OpenTofu stores in encrypted SSM parameters for the deployed Lambda runtime:
GITHUB_WEBHOOK_SECRET— required for local direct-env signature validation, reused byscripts/configure-self-webhook.sh, and used by OpenTofu to refresh the deployed webhook secret SSM parameterGITHUB_APP_ID— required for local direct-env GitHub App auth and used by OpenTofu to refresh the deployed GitHub App ID SSM parameterGITHUB_APP_PRIVATE_KEY— required for local direct-env GitHub App auth and used by OpenTofu to refresh the deployed GitHub App private key SSM parameter; use the PEM value directly or store it on one line with escaped newlines in.envGITHUB_APP_INSTALLATION_ID— optional explicit installation ID for local direct-env auth and deploysGITHUB_TOKEN— optional fallback for changed-file lookups; do not reuse it as the repository-admin token for webhook managementGITHUB_WEBHOOK_SECRET_SSM_PARAMETER_NAME— optional runtime override used when you want the app to read the webhook secret from SSM instead of direct envGITHUB_APP_ID_SSM_PARAMETER_NAME— optional runtime override forGITHUB_APP_IDGITHUB_APP_PRIVATE_KEY_SSM_PARAMETER_NAME— optional runtime override forGITHUB_APP_PRIVATE_KEYGITHUB_APP_INSTALLATION_ID_SSM_PARAMETER_NAME— optional runtime override forGITHUB_APP_INSTALLATION_IDGITHUB_TOKEN_SSM_PARAMETER_NAME— optional runtime override forGITHUB_TOKENAWS_REGION— optional, defaults tous-east-1EVALUATIONS_TABLE_NAME— required byloadAppConfig()RAW_EVENT_BUCKET_NAME— optionalENABLE_RAW_EVENT_ARCHIVE— optional boolean flagREQUIRED_LABELS— optional comma-separated labelsEVALUATION_REPOSITORY— optional local runtime override
Do not use .env as the home for ordinary OpenTofu root variables. Copy infra/terraform/env/dev.auto.tfvars.example to infra/terraform/env/dev.auto.tfvars and keep non-secret root variables there instead. Copy infra/terraform/backend/dev.s3.tfbackend.example to infra/terraform/backend/dev.s3.tfbackend and keep backend coordinates there.
For the one-time backend bootstrap script, .env or the shell may also provide:
TOFU_STATE_BUCKETTOFU_LOCK_TABLETOFU_STATE_REGIONTOFU_STATE_BUCKET_FORCE_DESTROYTOFU_STATE_BUCKET_VERSIONING_ENABLEDTOFU_LOCK_TABLE_POINT_IN_TIME_RECOVERY_ENABLEDTOFU_LOCK_TABLE_DELETION_PROTECTION_ENABLED
Supported AWS authentication paths are the standard AWS credential-chain options such as AWS_PROFILE, aws sso login, shared-config assume-role, and OIDC/web identity. Do not add AWS credentials to .env, *.auto.tfvars, or *.s3.tfbackend files.
Notes:
GET /healthcan run without the required webhook secrets because config loading happens inside the webhook handler.POST /webhooks/githubwill fail at runtime if the required direct GitHub values or SSM parameter names are missing, if GitHub App auth is absent, or if the configured GitHub App is not installed on the target repository.scripts/configure-self-webhook.shexpects the sameGITHUB_WEBHOOK_SECRET, but it uses a separate GitHub operator-auth lane: eithergh auth loginas a repository admin orGH_TOKENwithWebhooks: write.- Never commit real secrets, personal identifiers, or live cloud resource IDs. Use placeholders in docs and examples.
Development workflow
- The main entrypoint for behavior changes is usually
src/app.ts. - New PR evaluation rules should stay deterministic unless the repo explicitly adds a runtime AI integration.
- Keep webhook parsing, validation, GitHub API integration, and risk evaluation separate. The current split is:
- request parsing and HTTP responses in
src/app.ts - GitHub API interaction, including check publication, in
src/github/ - risk logic in
src/risk/ - orchestration in
src/services/ - persistence behind
src/storage/
- request parsing and HTTP responses in
- Edit source files in
src/, then rebuild; do not hand-editdist/. - Keep docs honest. The repo intentionally contains roadmap material, so if you implement or remove a feature, update the relevant docs.
- Keep the self-hook flow explicit.
scripts/deploy.shstays focused on AWS packaging and OpenTofu apply, whilescripts/configure-self-webhook.shis the separate operator step that mutates repository webhook settings.
Testing and verification
The repo now uses Vitest for integration coverage.
npm run test type-checks the production source, type-checks the Vitest harness, and runs the local Lambda integration suite.
npm run test:integration:deployed runs the deployed HTTP API suite and expects either DEPLOYED_HEALTH_URL / DEPLOYED_WEBHOOK_URL or .artifacts/<service>-deployment.json.
The deployed suite keeps its default checks safe by covering GET /health, empty-body webhook rejection, and invalid-signature rejection. A real deployed webhook success-path case is opt-in and skipped unless DEPLOYED_WEBHOOK_SECRET, DEPLOYED_PR_REPOSITORY, and DEPLOYED_PR_NUMBER are set.
The local Lambda suite now also verifies the mocked GitHub check-run create/update flow and the pass/fail/skip outcomes of the CVS phrase rule.
Self-dogfooding notes:
- The repo starts with zero GitHub repository webhooks. After deploy, run
bash scripts/configure-self-webhook.shexplicitly if you want this repository to send its ownpull_requestevents to the deployed PR Concierge endpoint. - That script writes
.artifacts/<service>-github-webhook.jsonwith the managed hook ID and GitHub API links for ping and delivery inspection. - Use the script's ping plus recent-deliveries guidance for configuration verification, then optionally reuse
npm run test:integration:deployedwith the live webhook env vars for a full PR-level proof.
Commands verified in this repository:
npm run buildnpm run testnpm run test:integration:deployedtofu fmt -checktofu init -backend=false -input=falsetofu validate
Infrastructure commands should be run from infra/terraform for the app stack and infra/bootstrap/tofu-backend for backend bootstrap work.
If you add tests:
- Prefer
*.spec.tsfiles or atests/directory; the risk classifier already treats those locations as test-only changes. - Keep integration coverage under
tests/integration/and prefer the exported Lambdahandlerfor local verification. - Keep tests focused on changed behavior.
- Update
package.jsonscripts and this file if you introduce a real test runner.
Before finishing a change, run the relevant app checks plus any targeted tests you added.
Code style and conventions
- TypeScript is compiled in strict mode.
- The repo uses ESM with
moduleandmoduleResolutionset tonodenext. - Keep explicit
.tsextensions in relative imports insidesrc/. - Prefer
import typefor type-only imports. - Follow the existing style: semicolons, trailing commas, named exports, and small focused helpers.
- Keep logs structured. Existing code uses
console.log(JSON.stringify({...}))and similar JSON logging patterns. - New JSON API responses should stay consistent with the existing routes: clear message, useful metadata, and
requestIdwhere available. - When changing environment variables or public behavior, update
.env.example,README.md, and any affected docs in the same change.
Infrastructure and deployment notes
infra/terraform now defines the active AWS footprint for PR Concierge through OpenTofu, including the Lambda function, HTTP API, DynamoDB table, optional S3 bucket, SNS topic, and CloudWatch alarms.
The root uses an S3 backend configured at init time, with DynamoDB used for state locking. The steady-state deploy and destroy scripts read backend coordinates from infra/terraform/backend/<env>.s3.tfbackend and non-secret root variables from infra/terraform/env/<env>.auto.tfvars.
The deploy and destroy scripts reserve TF_VAR_... for the remaining GitHub values that OpenTofu writes to encrypted SSM Parameter Store for the deployed runtime. AWS credentials stay on the AWS credential chain rather than in repo-local config files.
scripts/deploy.sh imports the Lambda function, IAM role, and Lambda log group into OpenTofu state when those resources already exist from an earlier manual or partially managed deployment.
The one-time backend bootstrap path lives in infra/bootstrap/tofu-backend/ and is wrapped by scripts/bootstrap-tofu-backend.sh. That root keeps local state on purpose so it can create the remote backend resources for the main stack.
The GitHub Actions workflows are:
.github/workflows/ci.ymlruns install, build, and the default test workflow on push and pull request.github/workflows/deploy.ymlruns install, build, the default test workflow, OpenTofu format/init/validate, authenticates to AWS with GitHub OIDC, bootstraps the OpenTofu backend if needed, deploys withscripts/deploy.sh, and then runs smoke plus safe deployed integration tests
Repository secrets required by .github/workflows/deploy.yml:
AWS_DEPLOY_ROLE_ARNPR_CONCIERGE_GITHUB_APP_IDPR_CONCIERGE_GITHUB_APP_PRIVATE_KEYPR_CONCIERGE_GITHUB_APP_INSTALLATION_ID(optional)PR_CONCIERGE_GITHUB_TOKEN(optional fallback)PR_CONCIERGE_WEBHOOK_SECRET
The workflow deploy path intentionally still runs through scripts/bootstrap-tofu-backend.sh and scripts/deploy.sh, so workflow changes should continue to keep those scripts as the source of truth.
Security considerations
- Preserve GitHub webhook signature validation in
src/github/signature.tsandsrc/app.ts. - Keep secrets in
.envlocally and in secret managers or CI configuration remotely. - Keep GitHub repository-admin auth for webhook management separate from the Lambda runtime GitHub App credentials and any optional
GITHUB_TOKENfallback. Use GitHub CLI auth or a dedicatedGH_TOKENwhen runningscripts/configure-self-webhook.sh. - Keep AWS provider and backend credentials out of
.env,*.auto.tfvars, and*.s3.tfbackendfiles. Use the AWS credential chain instead. - Remember that the GitHub runtime values still land in OpenTofu state today because OpenTofu manages the encrypted SSM parameter values. Protect the backend bucket and lock table accordingly.
- If you add AWS integrations, document the required IAM permissions and environment variables.
- Prefer least-privilege changes in Terraform and keep security-sensitive behavior explicit and reviewable.
Known implementation gaps
Be careful not to confuse planned architecture with shipped behavior:
- The deployed path can use DynamoDB-backed persistence, but local development often still uses
EVALUATION_REPOSITORY=consoleto avoid requiring AWS resources. - The docs describe optional raw event archiving, but the app currently only computes an S3 key placeholder when the feature flag is enabled.
required_labelsis empty by default. If an operator enables required labels later, they must create the matching GitHub labels separately; the basic self-hook flow does not bootstrap labels.- Some planning docs describe paths that no longer exist. The code in
src/is authoritative.
Pull request expectations
- Keep changes scoped and consistent with the MVP nature of the repo.
- Run the relevant verification commands before finishing.
- Update docs when routes, environment variables, workflows, or infrastructure assumptions change.
- If the repo later grows into multiple subprojects, add nested
AGENTS.mdfiles closer to those subtrees.