Commit Graph

15 Commits

Author SHA1 Message Date
Julien Gautier 289a5bad09 docs(development): refresh after phase-3a + add topology / trace diagrams
CI / scan (pull_request) Successful in 2m31s
CI / commits (pull_request) Successful in 2m31s
CI / check (pull_request) Successful in 2m40s
CI / a11y (pull_request) Successful in 1m51s
Docs site / build (pull_request) Successful in 2m3s
CI / perf (pull_request) Successful in 6m11s
The development guide drifted as portal-admin shipped, the docs site
landed, and Prisma stayed pinned at 6.x. Surgical pass.

Section 1 (Repo layout):
  * Add `apps/portal-admin/` and `apps/portal-admin-e2e/` — both
    exist on `main` since #134.
  * Drop the phantom `prisma.config.ts` (Prisma 6.x doesn't ship
    one) and fix the "Prisma 7" tag to "Prisma 6.x" with the
    ADR-0006 pin reference.
  * Add `docs/index.md`, `docs/architecture.md`, `docs/.vitepress/`
    + new workflows (`docs-site.yml`, `renovate.yml`) so the layout
    matches what `ls` actually shows.

Section 3 (Initial setup) — new diagram:
  * Mermaid `flowchart` of the local-dev topology (host-side dev
    servers + Compose containers + viewer profiles + the optional
    docs site). Replaces the prose ports-table that was scattered
    across §3 and §5 for new contributors.

Section 4 (Daily commands):
  * Add `portal-admin` to serve / test / generate examples.
  * Correct the single-test-file recipe — Nx's vitest executor
    rejects `--testFile`; use positional `path/to/file.spec.ts`
    for Vitest projects, `--testPathPattern=…` for Jest.
  * New "Documentation site" subsection — `docs:dev` / `docs:build`
    / `docs:preview`, link to ADR-0022.

Section 5 (Observability):
  * Mermaid `sequenceDiagram` of how a click becomes a trace
    (browser span → traceparent → BFF span → Pino log line with
    matching trace_id → OTLP batch → Jaeger). Anchors the
    correlation rule that the prose explained but didn't visualise.

Section 6 (Renovate):
  * Add "Transitive vulnerabilities — `pnpm.overrides`" subsection.
    Documents the pattern from #159 (vite + esbuild via VitePress)
    so the next contributor hitting a transitive vuln has the
    playbook on hand.

Section 9 (Sections to come):
  * Split into "Code shipped — doc to write" and "Not yet". Many
    phase-2 items moved out of "to come" since the code has shipped
    (auth, sessions, MFA decorator, admin module, audit viewer,
    user directory, downstream strategies, OpenAPI tooling,
    capabilities endpoint). The roadmap now reflects what's
    actually still missing.
2026-05-15 23:05:10 +02:00
julien 7579b25dfe feat(docs): vitepress site for docs/, mermaid rendering, ci build workflow (#154)
CI / commits (push) Has been skipped
CI / check (push) Successful in 2m39s
CI / scan (push) Failing after 2m31s
CI / a11y (push) Successful in 2m14s
CI / perf (push) Successful in 4m12s
Docs site / build (push) Successful in 2m27s
## Summary

Implementation of [ADR-0022](docs/decisions/0022-docs-site-vitepress.md). Stands up the static documentation site that renders `docs/**/*.md` (architecture diagrams, daily-dev guide, ADRs, onboarding) via **VitePress + `vitepress-plugin-mermaid`**, behind a Gitea Actions build gate.

Local dev: `pnpm docs:dev`. Full build: `pnpm docs:build` (~9 s, output in `docs/.vitepress/dist/`).

## What lands

### Dependencies

`vitepress 1.6.4`, `vitepress-plugin-mermaid 2.0.17`, `mermaid 11.15.0` — workspace devDependencies. No runtime impact on `portal-shell` / `portal-admin` / `portal-bff`.

### [`docs/.vitepress/config.mts`](docs/.vitepress/config.mts)

The single source of truth for the site. Highlights:

- **`srcExclude`** drops `docs/README.md` (git/IDE-only index per ADR-0022's option A) and `docs/decisions/template.md` (authoring scaffold).
- **`rewrites`** maps `decisions/README.md` → `decisions/index.md` so `/decisions/` resolves to the curated tag-grouped landing while the source filename stays git-conventional.
- **`ignoreDeadLinks`** skips:
  - `localhost:*` URLs (Jaeger, OTLP — only resolve in a live dev session),
  - cross-repo references (`../CLAUDE`, `../../apps/**`, `../../infra/**`, `../../notes/**`) — intentional from git/IDE consumers; not the site's job to render them,
  - excluded targets (`./template`, `./README`) — file exists in the repo, just not in the site.
- **Auto-sidebar for `/decisions/`** — `adrSidebarItems()` walks `docs/decisions/00*-*.md` and emits sorted `ADR-NNNN — title` entries. Adding an ADR is a single-file change, no `config.mts` edit.
- **Hand-curated top-level nav** (Development, Architecture, Decisions, Onboarding).
- **Mermaid via `withMermaid()`** with `securityLevel: 'strict'` so diagrams can't inject arbitrary HTML.

### [`docs/index.md`](docs/index.md)

VitePress Hero landing with four feature cards (Architecture, Decisions, Development, Onboarding).

### [`docs/development.md`](docs/development.md) — two surgical fixes

- Line ~5: `[setup/](setup/)` → `[setup/01-wsl-terminal-setup.md](setup/01-wsl-terminal-setup.md)`. Folder-style links don't resolve cleanly under `cleanUrls: true`; pointing at the first onboarding page is both correct and useful.
- Line 330: wrap `${{ github.* }}` in `<code v-pre>…</code>`. VitePress runs every Markdown file through the Vue template compiler, which sees the inline `{{ … }}` as an interpolation. `v-pre` keeps the literal text intact. The rest of the source is unaffected.

### [`package.json`](package.json)

Three new scripts:

```
docs:dev      → vitepress dev docs
docs:build    → vitepress build docs
docs:preview  → vitepress preview docs
```

Pure pnpm scripts, no Nx project — the site has no cross-project dependency graph to track.

### [`.gitea/workflows/docs-site.yml`](.gitea/workflows/docs-site.yml)

Triggers on push to `main` and on PR, scoped by `paths:` to `docs/**`, `package.json`, `pnpm-lock.yaml`, and the workflow itself. Three steps:

1. `pnpm install --frozen-lockfile`
2. `pnpm docs:build`
3. Regression fence: `grep` ADR-0009's rendered HTML for `class="mermaid"` or `<svg>` so a silent Mermaid-plugin breakage on a major upgrade fails the workflow rather than ship a site with raw code blocks where diagrams should be.
4. On push only: upload `docs/.vitepress/dist/` as a `docs-site` artifact (30-day retention). The actual rsync to the static host lands when the future infrastructure ADR locks the deployment target.

### [`.gitignore`](.gitignore)

Excludes `docs/.vitepress/{cache,dist}/` so local builds don't leak into commits.

## Notes for the reviewer

- **Why `config.mts` and not `config.ts`?** VitePress is ESM-only, and `vitepress-plugin-mermaid` follows. Vite loads `.ts` config files via its CJS bundler in this workspace's setup and chokes on the ESM imports. `.mts` flips the loader to ESM and the build succeeds. Same pattern is used elsewhere in the workspace (`jest.config.cts`, app `vite.config.mts`).
- **Why no Nx project (`docs/project.json`)?** The doc site has no Nx-trackable dependencies (it consumes `.md` files, not TypeScript projects). Putting it in the Nx graph adds ceremony with no caching benefit — VitePress's incremental rebuilds are sub-second already, and the site never has cross-project `affected` semantics. Pure pnpm scripts keep the surface small.
- **Why the regression fence on Mermaid?** ADR-0022 §"Confirmation" promises it. The plugin is a community dep (sub-1.0 wrapper around the official Mermaid renderer); a major upgrade or a Mermaid runtime change could leave fenced ` ```mermaid ` blocks rendered as raw code without anyone noticing — until an RSSI clicks ADR-0009 and sees no diagram. Cheap grep gate, real signal.
- **Why upload as artifact, not deploy?** Per [ADR-0022](docs/decisions/0022-docs-site-vitepress.md) §"Deployment & CI": the host (`docs.portal.apf.fr` or a sub-path) is provisional. Locking an rsync target now would couple this PR to a not-yet-made infra decision. Artifact upload is the staging mechanism — manual drop on the host until the infrastructure ADR formalises the target.
- **Why `ignoreDeadLinks` rather than fixing every cross-repo reference?** The cross-repo links are genuinely useful from a git/IDE perspective (where the docs/ markdown is browsed alongside the rest of the codebase). Rewriting them to `https://git.unespace.com/julien/apf_portal/src/branch/main/…` would make them work on the site but lose the IDE quick-jump. Skipping at site-build time is the right trade-off — the site reader gets a graceful "link doesn't exist here" if they click, the IDE reader gets a working jump.

## Test plan

- [x] `pnpm docs:build` succeeds in ~9 s. Output at `docs/.vitepress/dist/` contains an `index.html`, every ADR, the development guide, the architecture diagrams, and the three setup pages.
- [x] Mermaid renders: `grep 'class="mermaid"' docs/.vitepress/dist/decisions/0009-…html` returns a match.
- [x] `pnpm exec nx run-many -t format:check lint test build` for the 6 main projects — 18/18 tasks green, no Nx regression from the new top-level config.
- [ ] **Manual smoke**: `pnpm docs:dev`, open `http://localhost:5173`, walk through:
  - Landing renders Hero + 4 feature cards.
  - Search box returns hits for "audit", "MFA", "OBO".
  - `/decisions/0009-…` renders the OIDC sequence diagram (Mermaid SVG, not raw text).
  - `/decisions/0010-…` ERD or `/architecture` C4 diagrams likewise.
  - Dark-mode toggle flips diagrams to dark theme without page reload.
  - Sidebar shows the 22 ADRs auto-listed under `/decisions/`.
  - The "Decisions" curated index at `/decisions/` lists ADRs by tag (no regression on the source markdown).

## What's next

Once the deployment target is fixed (future infra ADR), wire the rsync step into the workflow — that lands as a small follow-up PR. Until then the artifact carries the bundle.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #154
2026-05-15 19:14:14 +02:00
julien 922a44bb50 chore(ci): auto-merge low-risk renovate updates (#77)
CI / check (push) Successful in 1m37s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 1m14s
CI / a11y (push) Successful in 58s
CI / perf (push) Successful in 2m37s
## Summary

After ~10 days of clean Renovate track record (~30 PRs merged without regression beyond the TS/ESLint/webpack-cli majors that the dashboard-approval rule now catches), enable auto-merge on the lowest-risk update types. CI is the gate — `check` / `scan` / `a11y` going red leaves the PR open for manual triage.

| Update type | Pre-PR | Post-PR |
| - | - | - |
| **patch** | manual review | **auto-merge if CI green** |
| **pin** (rolling-tag → fixed-version pin) | manual | **auto-merge if CI green** |
| **digest** (image digest pin refresh) | manual | **auto-merge if CI green** |
| **lockFileMaintenance** (weekly transitive refresh) | manual | **auto-merge if CI green** |
| **minor** | manual review (unchanged) | manual review |
| **major** | dashboard approval (unchanged) | dashboard approval |

`automergeStrategy: "squash"` matches our trunk-based squash-merge convention. `automergeType: "pr"` keeps the PR + CI run as the audit trail (vs branch-direct push), and Gitea auto-merges the PR once green via the bot's existing `repo:write` permission.

## Doc updates

- `renovate.json` `dependencyDashboardHeader` — the "Open" row now reflects the new reality: mostly minors, with red patches surfacing briefly when CI fails.
- `docs/development.md` §"Reviewing Renovate PRs" gains a bullet describing the auto-merge for contributors landing on the project later.

## Test plan

- [ ] CI green on this PR.
- [ ] After merge, the next patch-level Renovate run produces a PR that auto-squashes into `main` once CI clears (visible in the merged log; no human action required).
- [ ] A patch with red CI stays open in "Open" with the `dependencies` label.
- [ ] Minor / major Renovate PRs continue to require human merge.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #77
2026-05-10 03:58:01 +02:00
julien e2dd2e4dd8 fix(portal-shell): use .postcssrc.json so tailwind utilities are emitted (#75)
CI / check (push) Successful in 2m16s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 1m23s
CI / a11y (push) Successful in 1m8s
CI / perf (push) Successful in 2m36s
## Summary

The portal-shell scaffold shipped a `postcss.config.js`, but `@angular/build` (Angular 17+ esbuild pipeline) does **not** load that file format — it only reads `.postcssrc.json`. Result: the `@tailwindcss/postcss` plugin was never registered, Tailwind ran with no source files visible, generated only its `@theme` block, and dropped every utility class on the floor.

The page therefore rendered unstyled — black text on white — even though `nx build` reported success and shipped a 23 KB `styles*.css`.

Rename / re-encode the config to `.postcssrc.json`. Same plugin, same options, just the file format Angular's esbuild adapter actually reads.

## Verification

| | Before | After |
| - | - | - |
| Class selectors in `dist/apps/portal-shell/browser/styles*.css` | **0** | **75** (production) |
| `.text-3xl`, `.bg-amber-50`, `.font-semibold` etc. emitted |  | ✓ |
| Pages render with intended Tailwind layout |  | ✓ |

```bash
pnpm exec nx build portal-shell --configuration=production
grep -oE '\.[a-zA-Z][a-zA-Z0-9_-]*' dist/apps/portal-shell/browser/styles*.css | sort -u | wc -l
# 75
```

## Doc update

`docs/development.md` repo-layout walkthrough now reads `.postcssrc.json` and explicitly calls out that `postcss.config.js` is ignored by `@angular/build` — so a future contributor reaching for the legacy filename gets a hint instead of a silent failure.

## Test plan

- [ ] CI green on this PR.
- [ ] Local: bring up the SPA dev server, `localhost:4200` shows the styled layout — header bar with brand link, system-status widget with rounded card, footer with the two language links.
- [ ] Production build: `nx build portal-shell --configuration=production` succeeds; `dist/.../styles*.css` contains tens of utility-class selectors (not just `@theme` tokens).

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #75
2026-05-10 02:55:15 +02:00
julien 288610b9ba docs(development): add observability dev-loop section (#73)
CI / check (push) Successful in 1m27s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 1m25s
CI / a11y (push) Successful in 45s
CI / perf (push) Successful in 2m20s
## Summary

ADR-0012 phase 1 + phase 2 are wired (BFF + SPA), so the "Observability dev-loop" placeholder in the roadmap table now has real content to point at. Promote it from §9 future-work list to a full §5 walkthrough sitting between "Daily commands" and "Dependency updates".

## What §5 covers

- **Bringing up the observability stack** — `./infra/local/dev.sh up observability`, with the endpoint table (Jaeger UI, OTLP receiver, BFF stdout, browser DevTools).
- **Reading a trace in Jaeger** — service-dropdown filter, span attribute keys to look at (`http.method`, `db.statement`, `service.name`, `trace_id`), the trace-id-as-pivot pattern.
- **Correlating a trace with the BFF Pino logs** via `trace_id` and `request_id` (the per-request UUID `nestjs-cls` provides). Concrete `grep` example.
- **Reading SPA spans from the browser** — DevTools Network filter on `localhost:4318/v1/traces` + Jaeger UI cross-check.
- **Common gotchas** — observability profile not active, CORS pre-flight on `/v1/traces`, `propagateTraceHeaderCorsUrls` mismatches, NODE_ENV mis-set, EADDRINUSE on serve restart.
- **What's not in v1** — pointer at the "wired as features land" deltas (CLS keys for session/user/audience, redact list, custom domain spans, prod backend) so a contributor knows what's intentionally not yet there.

## Renumbering

The new §5 pushes existing sections down. Final structure: 1. Repo layout / 2. Prerequisites / 3. Initial setup / 4. Daily commands / 5. **Observability dev-loop** / 6. Dependency updates (Renovate) / 7. Conventional commit cycle / 8. Where to look / 9. Roadmap.

The "Observability dev-loop" line in §9's roadmap table is removed (now implemented).

`docs/README.md` cross-link updated to mention the new section.

## Test plan

- [ ] CI green on this PR (`format:check`).
- [ ] In a fresh checkout, follow §5's "Bring up the observability stack" verbatim, reach the Jaeger UI, then walk a trace through the `grep` correlation example with a synthetic request — flag any step that's wrong / missing on this real-world rehearsal.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #73
2026-05-10 02:14:46 +02:00
julien fc9b63f24a feat(infra): add dev.sh wrapper for the local-dev compose stack (#68)
CI / check (push) Successful in 1m36s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 2m9s
CI / a11y (push) Successful in 1m9s
CI / perf (push) Successful in 3m1s
## Summary

Two recurring frictions on the local-dev stack:

1. **Compose-profile asymmetry** — `docker compose down` only operates on services whose profile is currently active. Anything brought up with `--profile X` keeps running unless the same flag is passed on `down`. pgweb and Jaeger silently survived several `down -v` invocations before we noticed (#67 documented the gotcha; this PR makes it impossible to hit if you use the wrapper).
2. **Verbose invocations** — typing `docker compose -f infra/local/dev.compose.yml --profile … <verb>` for routine ops gets old fast.

Add [`infra/local/dev.sh`](infra/local/dev.sh) as a thin wrapper. Always passes every profile in scope on teardown / status / log commands, exposes ergonomic verbs:

| Command                                       | Effect                                                          |
| --------------------------------------------- | --------------------------------------------------------------- |
| `./infra/local/dev.sh up`                     | Core only (postgres + redis + otel-collector)                   |
| `./infra/local/dev.sh up all`                 | Core + every profile                                            |
| `./infra/local/dev.sh up dbtools`             | Core + pgweb                                                    |
| `./infra/local/dev.sh up observability`       | Core + Jaeger                                                   |
| `./infra/local/dev.sh down [-v]`              | Tear down (every profile in scope, no orphaned services)        |
| `./infra/local/dev.sh stop <service>`         | Stop one service (containers stay around)                       |
| `./infra/local/dev.sh restart <service>`      | Restart one service                                             |
| `./infra/local/dev.sh status`                 | `ps` with every profile visible                                 |
| `./infra/local/dev.sh logs [service]`         | Follow logs                                                     |
| `./infra/local/dev.sh exec <service> <cmd>`   | Run a command inside a container                                |

Anything not matching one of the named verbs is passed through to `docker compose -f dev.compose.yml ...` (with every profile flagged in), so the full Compose surface remains available.

## Doc updates

- **`infra/README.md`** — new "Convenience script" subsection with the cheat-sheet table; "First-time setup" rewritten to use the script; the standalone "Profile symmetry" tip from #67 is collapsed into a one-liner since the script now handles it (the note remains as a fallback for direct `docker compose` users).
- **`docs/development.md` §3** — points at the script for the typical setup flow.

The compose file itself is unchanged.

## Test plan

- [ ] `./infra/local/dev.sh help` prints the usage block.
- [ ] `./infra/local/dev.sh up` brings up the 3 core services (no pgweb / no jaeger).
- [ ] `./infra/local/dev.sh up all` adds pgweb and Jaeger.
- [ ] `./infra/local/dev.sh down -v` stops and removes all 5 containers (incl. pgweb and Jaeger), wipes the postgres-data and redis-data volumes.
- [ ] `./infra/local/dev.sh stop pgweb` stops just pgweb.
- [ ] `./infra/local/dev.sh logs otel-collector` follows that service's logs.
- [ ] `./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\du"` lists the audit roles.
- [ ] `./infra/local/dev.sh stop` (no arg) errors with a clear message.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #68
2026-05-09 21:50:37 +02:00
julien a93b1067e6 docs(setup): add psql and redis-cli to prerequisites (#61)
CI / check (push) Successful in 1m22s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 1m42s
CI / a11y (push) Successful in 44s
CI / perf (push) Successful in 2m29s
## Summary

Verifying the local-dev stack from the host (`docker compose up -d` + `psql ... -c "\du"` / `redis-cli PING`) requires the postgres and redis client binaries on the developer's machine. They were missing from the prereqs table, so `apt install postgresql-client` / `apt install redis-tools` was an implicit step nobody knew to run.

Add both to §2's table, with one-line rationale for each. The Docker row is also tightened to point at the actual local-dev stack location ([`infra/local/`](infra/local/)) instead of the placeholder "Postgres + Redis containers" wording from before that recipe existed.

`docker compose exec` remains a viable zero-install alternative for developers who prefer not to touch their host. Mentioned only informally — the host-install path is the documented one.

## Test plan

- [ ] Fresh-clone a checkout, follow §2 + §3 verbatim, end with a working stack and successful `psql ... -c "\du"` against it.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #61
2026-05-08 22:05:05 +02:00
julien 0f00d6d93f feat(infra): add local-dev Docker Compose stack (#57)
CI / commits (push) Has been skipped
CI / check (push) Successful in 1m12s
CI / scan (push) Successful in 1m20s
CI / a11y (push) Successful in 41s
CI / perf (push) Successful in 2m9s
## Summary
Bring up Postgres + Redis + OTel Collector in one command so contributors can run the BFF end-to-end without manually wiring each service. Replaces the throwaway `docker run postgres:17-alpine` one-liner that was in `docs/development.md` §3.

### What lands
- **`infra/local/dev.compose.yml`** — three core services (`postgres:17.2-alpine`, `redis:7.4-alpine`, `otel/opentelemetry-collector-contrib:0.115.0`) plus two viewers gated behind Compose profiles:
  - `--profile dbtools` → `sosedoff/pgweb:0.16.2` (Postgres GUI on port 8081)
  - `--profile observability` → `jaegertracing/all-in-one:1.62` (Jaeger UI on 16686)
  - All ports overridable via `.env`. State in named volumes. Healthchecks on data services.
- **`infra/local/.env.example`** — credentials + ports template. `POSTGRES_PASSWORD` and `REDIS_PASSWORD` are mandatory (compose refuses to boot without them); other keys default sensibly.
- **`infra/local/init/postgres/01-init.sql`** — bootstrap SQL per **ADR-0013**: `audit_owner` / `audit_writer` / `audit_reader` / `audit_archiver` roles + `audit` schema. Default privileges encode the append-only contract (INSERT to writer, SELECT to reader, DELETE to archiver, no UPDATE/TRUNCATE to anyone). Applied on first Postgres boot only; documented re-run procedure.
- **`infra/local/otel-collector.yaml`** — pipeline: OTLP gRPC/HTTP → batch → debug exporter (always) + forward to `jaeger:4317`. When the observability profile is off, the Jaeger export logs warn-level retries but doesn't block the debug pipeline.

### Surrounding doc updates
- **`infra/README.md`** — new "Local-dev stack" section: service inventory, port table, first-time setup walkthrough, persistence/bootstrap-replay tips. The previous `local/` placeholder line is removed.
- **`docs/development.md`** §3 — rewritten to walk through the compose-based setup; cross-links to `infra/README.md` for the full reference. Roadmap entry for "Local infra recipe" removed from §8 (now implemented); "Observability dev-loop" line adjusted to point at the new Jaeger profile.

### Out of scope
- **Production parity** — HA Postgres, Redis Sentinel, real OTel backend (Tempo / Loki / etc.) — defer to the on-prem infrastructure ADR (phase 3b). The dev-only nature of this stack is called out explicitly in `infra/README.md`.
- **Wiring the BFF** to actually use these endpoints (NestJS config, Prisma datasource URL, OTel SDK init) — that's the **B — Observability foundations** chantier, next up.

## Test plan
- [ ] `cd infra/local && cp .env.example .env && docker compose -f dev.compose.yml up -d` → all three core services come up healthy; verify with `docker compose ps`.
- [ ] `psql postgres://portal:<pwd>@localhost:5432/portal_dev -c "\dn"` shows the `audit` schema; `\dg` shows the four audit roles.
- [ ] `redis-cli -a <pwd> PING` → `PONG`.
- [ ] Send a fake OTLP trace via grpcurl → see it printed by `docker compose logs otel-collector`.
- [ ] `--profile dbtools up -d` → http://localhost:8081 shows pgweb UI, can navigate to the audit schema.
- [ ] `--profile observability up -d` → http://localhost:16686 shows Jaeger UI; collector logs no longer report Jaeger export retries.
- [ ] `docker compose down -v` cleanly removes everything; next `up -d` re-runs the bootstrap SQL.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #57
2026-05-08 19:23:43 +02:00
julien f5d3697466 fix(deps): revert TS6/ESLint10/webpack-cli7 majors and gate future majors (#43)
CI / commits (push) Has been skipped
CI / check (push) Successful in 2m25s
CI / a11y (push) Successful in 48s
CI / perf (push) Successful in 3m25s
CI / scan (push) Failing after 7m18s
## Summary
Three Renovate major bumps merged silently because `nx affected` doesn't see deps-only PRs as affecting any project — CI passed trivially, the breakage only surfaced when `nx run-many` was run locally:

- **TypeScript 5→6** (#33) — `tsconfig.lib.json` fails with `TS5101: Option 'baseUrl' is deprecated`. Revert to 5.9.x.
- **ESLint 9→10** (#36) — `@nx/eslint@22.7.1` not compatible: project graph fails with "Unable to find eslint". Revert eslint, `@eslint/js`, `jsonc-eslint-parser`, `eslint-plugin-playwright` to ESLint-9-compatible versions.
- **webpack-cli 5→7** (#34) — webpack-cli 7 removed the `--node-env=production` flag Nx generates. Revert to 5.x.

Bonus side-fix: the `ajv@<8.18.0` override added in #42 was over-broad and was forcing ESLint's bundled ajv to v8 (incompatible with ESLint 9's option contract). Narrow the override to `@angular-devkit/core>ajv@<8.18.0` so only the targeted nestjs-prisma chain is bumped.

## Prevention — gate majors behind the dependency dashboard

Add a Renovate `packageRule` with `dependencyDashboardApproval: true` for `matchUpdateTypes: ["major"]`. Renovate stops auto-creating PRs for majors; they appear as checkboxes in the dashboard issue, and only get a PR after a human ticks the box (presumably after reading the changelog and confirming Nx-plugin / Angular / NestJS readiness).

This is the surgical fix for the gap. The deeper fix (making `nx affected` correctly mark all projects as affected on package.json changes) is a separate investigation worth doing later — but the dashboard gate prevents the same trap regardless.

## Verification
Locally on this branch:
- `pnpm exec nx run-many -t lint test build --parallel=2` → ✓ 8 projects pass.
- `pnpm audit --audit-level=moderate` → 0 vulnerabilities.

## Test plan
- [ ] `check` job goes green on this PR (would have caught the regressions if `nx affected` were broader).
- [ ] After merge, the next Renovate run does not create new PRs for any major (TS, eslint, webpack-cli, etc.).
- [ ] Any pending major in the dashboard issue still appears, but only as a checkbox awaiting approval.

## Out of scope (follow-up)
Investigate why `nx affected` misses package.json-only changes. Likely a missing entry in `nx.json` `namedInputs` (`default`) or `targetDefaults`. Worth its own focused PR; the dashboard gate is the conservative fix in the meantime.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #43
2026-05-06 22:43:35 +02:00
julien f9ed3cf82a chore(ci): skip perf and commits gates on Renovate-authored PRs (#23)
CI / commits (push) Has been skipped
CI / check (push) Successful in 2m20s
CI / scan (push) Failing after 2m34s
CI / a11y (push) Successful in 1m7s
CI / perf (push) Successful in 3m43s
## Summary
Renovate's dep-bump PRs run the full pipeline today (`check`, `scan`, `commits`, `perf`, `a11y`). Two of those gates have near-zero signal on a typical bump and dominate the wall-clock cost:

- **`perf`** — Lighthouse build + 3-iteration median across the critical-routes list. 3-5 min per PR for a metric that is essentially zero on a patch/minor dep bump (the SPA today serves the static placeholder; even with real routes a typical bump stays inside the median noise floor).
- **`commits`** — re-validates commit messages that Renovate generates from a Conventional-Commits-conformant template. Tautological.

Skip both when the PR author is the `apf-portal-bot` Gitea user. The `push` event on `main` still runs the full pipeline post-merge, so any regression caught by `perf` is detected seconds after merge — fast enough to revert.

Net result: Renovate PRs run `check + scan + a11y` only, ≈ 4-5 min faster per PR.

## ADR amendment

ADR-0017 is amended in the same change:
- "Where Lighthouse CI runs" table now distinguishes human PR / bot PR / push to main / scheduled / local.
- New "Pre-merge gating policy: human PRs vs bot PRs" subsection records the rationale and the human-takeover edge case.
- §Confirmation entry for `perf` is reworded to reflect the conditional gate.

## Test plan
- [ ] After merge, the next Renovate-triggered PR (auto-rebase or new bump) shows only `check`, `scan`, `a11y` queued — no `perf`, no `commits`.
- [ ] A human-opened PR (e.g. this one) still queues all 5 gates.
- [ ] On `push` to `main` post-merge, the full pipeline runs including `perf`.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #23
2026-05-05 16:12:19 +02:00
julien f58cf13e33 fix(ci): give Renovate a git identity and a github.com token (#13)
CI / check (push) Successful in 2m24s
CI / commits (push) Has been skipped
CI / scan (push) Failing after 1m8s
CI / a11y (push) Successful in 1m32s
CI / perf (push) Successful in 2m52s
## Summary
First successful Renovate run (after the docker-image fix in #12) extracted 116 deps cleanly but every branch update failed with `fatal: empty ident name not allowed`, then the whole repo aborted on `Lock file error - aborting`. Two root causes:

- **No git identity** — the bot user had an email but no **Full Name** on its Gitea profile, so Renovate produced incomplete git authorship.
- **No github.com auth** — Renovate could not look up versions of GitHub-hosted Actions (`actions/checkout`, etc.) or `containerbase/node-prebuild` (the Node binary it dynamically fetches for lockfile maintenance) — anonymous rate limit (60 req/h) hit.

Fixes:

1. **`renovate.json`** — pin `gitAuthor` explicitly. The Full Name was also set on the bot's Gitea profile for UI consistency, but the override in config means we don't depend on out-of-band UI state.
2. **`.gitea/workflows/renovate.yml`** — pass `RENOVATE_GITHUB_COM_TOKEN` from the new `GITHUBCOM_TOKEN` repo secret (no underscore between GITHUB and COM — Gitea reserves the `GITHUB_*` namespace).
3. **`docs/development.md`** — onboarding procedure now covers both tokens + the Full Name step.

## Manual setup required after merge
Already done in this iteration:
- Bot's Full Name set in Gitea ("APF Portal Bot").
- `GITHUBCOM_TOKEN` repo secret created with a zero-scope github.com PAT.

(If the PAT was leaked during setup, regenerate before merging — the workflow only references the secret name, not the value.)

## Test plan
- [ ] After merge, trigger Renovate manually (Actions → Renovate → Run workflow).
- [ ] No `empty ident name` warning in the logs.
- [ ] No `Lock file error - aborting`; repo finishes with `INFO: Repository finished … "cloned": true`.
- [ ] Renovate creates the dependency dashboard issue + the first batch of grouped PRs (Angular, Nx, NestJS, Prisma, …) signed by `apf-portal-bot`.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #13
2026-05-05 13:47:30 +02:00
julien 82911f9319 chore(ci): set up Renovate dependency automation (#11)
CI / commits (push) Has been skipped
CI / check (push) Successful in 1m21s
CI / scan (push) Failing after 1m27s
CI / a11y (push) Successful in 44s
CI / perf (push) Successful in 2m28s
## Summary
Wire up Renovate to keep dependencies fresh without manual triage.

- **Workflow** — `.gitea/workflows/renovate.yml`: cron daily at 03:00 UTC + `workflow_dispatch`, runs on the existing self-hosted runners. Picked 03:00 specifically so Monday's tick sits inside Renovate's default `lockFileMaintenance.schedule` ("before 4am Monday") and triggers the weekly lockfile refresh in passing.
- **Config** — `renovate.json`: `config:recommended` baseline + groupings (Angular, Nx, NestJS, Prisma, Vitest, TypeScript tooling, ESLint, SWC, Tailwind), Conventional-Commits commit messages, OSV.dev as vulnerability source, dependency dashboard issue.
- **Docs** — new §5 in `docs/development.md` covering the bot onboarding (manual, one-shot), how to trigger Renovate manually, how to review its PRs. The placeholder roadmap entry is dropped from §8.

No ADR — Renovate is operational tooling, not an architectural decision (and on Gitea it's the only viable bot anyway, no real alternative to capture).

## Manual setup required after merge

The workflow is wired but inert until the bot is onboarded once on Gitea:

1. Create a non-admin Gitea user `apf-portal-bot`.
2. Add the bot as a **Write** collaborator on this repo.
3. Sign in as the bot, generate a PAT (scopes: read/write `repository`, read/write `issue`, read `user`).
4. Add the PAT as the repo secret `RENOVATE_TOKEN` (Settings → Actions → Secrets).

Detailed steps in [`docs/development.md`](docs/development.md) → "Dependency updates (Renovate)".

## Test plan
- [ ] After bot onboarding, trigger the workflow manually (Repo → Actions → Renovate → Run workflow).
- [ ] First run creates the "Renovate Dependency Dashboard" issue and a batch of grouped PRs (Angular, Nx, …).
- [ ] Each generated PR has CI green (`check`, `scan`, `commits`, `perf`, `a11y`).
- [ ] Commit subject matches `chore(deps): …` / `fix(deps): …` so the `commits` gate doesn't reject.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #11
2026-05-04 17:37:13 +02:00
Julien Gautier 156e7ca2df chore: add PR template and document title/body convention
CI / check (push) Failing after 5s
CI / commits (push) Has been skipped
CI / scan (push) Failing after 6s
CI / perf (push) Failing after 3s
CI / a11y (push) Failing after 1s
Formalise the PR-flow conventions while we install the PR-flow itself.

.gitea/pull_request_template.md auto-populates the PR body in Gitea
with five sections: Summary / Motivation / Implementation notes /
Verification (with CI-gate checkboxes + ADR/diagram update flags) /
Related. Sections can be left blank when irrelevant; the template
guides without adding ceremony. Header HTML comment reminds the
contributor of the PR title format and links to the full convention.

docs/development.md §5 (Conventional commit cycle) gains a 'PR
conventions' subsection that:
- explains why the PR title format matters (squash-merge subject on
  main, validated by commitlint in the CI 'commits' job)
- separates feature-branch commit hygiene (exploratory OK) from PR
  title hygiene (must conform)
- documents the type vocabulary (feat/fix/docs/style/refactor/perf/
  test/build/ci/chore/revert)
- proposes an optional scope vocabulary (apps, libs, cross-cutting
  domains like decisions/docs/ci/deps)
- describes the body template

No new ADR. The PR title format is derived from ADR-0007 (Conventional
Commits at the commit-msg layer) plus ADR-0015 (squash-merge means PR
title becomes the commit subject on main). The body template is
tactical guidance, not architectural.
2026-04-30 23:03:13 +02:00
Julien Gautier 7d27cd8773 docs: turn development.md §7 into a phase-mapped roadmap
CI / check (push) Failing after 2s
CI / commits (push) Has been skipped
CI / perf (push) Failing after 12s
CI / a11y (push) Failing after 3s
CI / scan (push) Failing after 1m33s
The section was a short bullet list of 'sections to be added' - it
underplayed how broad the future content really is, and gave no
visibility on what triggers each. Replace it with a structured table
that maps every planned section to (a) its ADR phase and (b) the
specific implementation work that unlocks it. A contributor reading
the doc today now sees:

- which dev-loops will exist (auth, sessions, MFA step-up, OTel,
  audit, downstream APIs, component patterns, a11y, perf debugging,
  Renovate, release, GitLab migration, architecture diagrams)
- under which ADR each lands
- what concrete event in the codebase makes each section real

Plus the explicit policy: each entry stays a subsection of this doc
until we have at least three substantial sub-topics, at which point
the file is split into docs/development/ with an index. Avoids
creating empty placeholder files (per CLAUDE.md: 'documentation when
genuinely useful, not just to tick a box') while signalling the
future structure clearly.

Cross-references each row to its triggering ADR so the table doubles
as a 'what's pending implementation' radar. Foreshadows the §7 → file
split that will happen once content density justifies it.
2026-04-30 20:11:01 +02:00
Julien Gautier b69d3a2206 docs: add development.md as the day-to-day reference for working on apf_portal
CI / check (push) Failing after 2s
CI / commits (push) Has been skipped
CI / perf (push) Failing after 12s
CI / a11y (push) Failing after 2s
CI / scan (push) Failing after 1m44s
A new contributor (or returning lead) opening the repo gets:
- the final repo layout, with one-line annotations per top-level dir
- the prerequisite tooling list (Node 24 LTS, pnpm 10, mkcert,
  optional local Trivy/gitleaks, Docker for Postgres)
- the fresh-clone setup steps (clone, pnpm install, prisma generate,
  sanity check)
- the daily commands organised by intent: serve, test (incl. single
  file), lint, build, generate (apps / libs / components), Prisma,
  the four ci:* scripts that mirror the CI gates
- the conventional commit cycle end-to-end (branch naming, hook
  enforcement, PR gates, squash-merge, release tagging)
- a 'where to look' table cross-linking the project rules
  (CLAUDE.md), the ADRs, the setup guides, and the personal notes
- an explicit 'to be added' section listing what the doc will grow
  into (local infra Docker Compose, auth dev-loop, component
  patterns, debugging tips, release workflow, Renovate policy)

The doc is intentionally non-exhaustive at v1 - it captures what a
contributor needs today and is structured to grow as the workflow
sharpens. Indexed in docs/README.md under a new 'Daily development'
section, separate from the one-off onboarding guides under
docs/setup/.
2026-04-30 19:58:37 +02:00