# adr.smlcompany.ca The dispute resolution practice of Pouya Lajevardi — Toronto. A static site built with [Astro](https://astro.build), built for deployment to Amazon S3 behind CloudFront by Gitea Actions — see Deployment; the pipeline is not yet proven. ## Quick start ```bash nvm use # Node 22 npm install npm run dev # http://localhost:4321 ``` ## Scripts | Command | Does | |---|---| | `npm run dev` | Development server with hot reload | | `npm run build` | Static build to `./dist` | | `npm run preview` | Serve the built site locally | | `npm run check` | `astro check` — type and template errors | | `npm run lint` | ESLint + Prettier check — **not yet wired**, no ESLint config exists | | `npm run format` | Prettier — rewrite files in place | | `npm run lighthouse` | Lighthouse CI — **not yet wired**, no `lighthouserc` exists | ## Before you contribute Read **`AGENTS.md`** first, and maintain it as you work — it is the living record of what this project is, what was decided, and why. Then read **`CLAUDE.md`** for the working rules, and the specs in `docs/`. The single hardest rule: **no factual claim about Pouya, his credentials, his experience, or his practice ships unless it appears in the verified register in `AGENTS.md` §4.** This is a public marketing surface, and the site it replaces contained fabricated credentials. ## How work is done here Pouya decides; Claude Code implements and then adversarially reviews its own work. Run **`/build `** for any substantive change — it plans, implements, runs two independent review agents on the diff (the claims audit wherever copy changed), resolves the findings, verifies the build, and records the session in `AGENTS.md`. `/review` runs the review pass alone; `/wrap` closes a session. Full protocol and prompt guidance: `docs/08-execution-protocol.md`. ## Deployment `.gitea/workflows/deploy.yml` is the deploy pipeline — **Gitea Actions**, not GitHub Actions. On a push to `main` it builds, syncs to S3, and invalidates CloudFront. **It has never run green.** There is no `package-lock.json`, so `npm ci` exits at step one; `src/pages/` is empty, so there is nothing to build; and whether an `act_runner` is registered is recorded nowhere. The workflow is written; the pipeline is unproven. The workflow's **first** step guards against the other way this fails quietly: it aborts the run, naming the variable, if `AWS_REGION`, `S3_BUCKET`, or `CLOUDFRONT_DISTRIBUTION_ID` is empty — which is how a Gitea too old for the `vars` context manifests. The GitHub Actions equivalent, which uses OIDC role assumption, is kept as `docs/reference/github-actions-oidc.yml.example` in case the project ever moves to a forge that supports it. It sits outside `.github/workflows/` on purpose: Gitea falls back to that directory when `.gitea/workflows` is absent, so a workflow file left there with a `push` trigger would be only conditionally inert. As an `.example` under `docs/` it cannot be picked up at all. **The pipeline is designed around a long-lived AWS credential.** Gitea is not an AWS OIDC provider, so there is no role to assume: deploys are to authenticate as a scoped IAM user, `adr-sml-deploy`, with its access key held in the repository's Gitea Actions secrets. Nothing in `AGENTS.md` records that user as created or that key as issued — `docs/06-deployment.md` is a procedure to perform, not a record of one performed. Two things are meant to bound the risk, and neither is confirmed in place: - **The policy must stay narrow.** Four actions: `s3:ListBucket` on one bucket, `s3:PutObject` and `s3:DeleteObject` on that bucket's contents, and `cloudfront:CreateInvalidation` on one distribution. No `Action: "*"`, no `Resource: "*"`, nothing outside that one bucket and that one distribution. The AWS account is shared with unrelated projects, including a bucket whose name indicates another business's production database backups — that narrowness is what keeps a compromised runner away from it, and it is load-bearing rather than hygiene. See `AGENTS.md` §10. If a deploy step needs a permission the policy lacks, question the step; do not widen the policy. - **The key must be rotated quarterly, and nobody owns that yet.** Create a second access key, update the Gitea secrets, confirm a deploy succeeds, then delete the old one — rotation that leaves the old key active is not rotation. OIDC would have removed the obligation entirely; it is unavailable, so this is a standing calendar task still waiting on an owner. Full procedure, IAM policy, runner setup, and cutover checklist: `docs/06-deployment.md`.