Commit Graph
175 Commits
Author SHA1 Message Date
kpcto 768f009ee9 Merge pull request '[E01-S01-T04] ADR: PostgreSQL' (#408) from feature/191 into main
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (push) Successful in 1m7s
CI / Stage 4 — Unit tests (E00-S05-T01) (push) Successful in 1m48s
CI / Stage 5 — Architecture tests (E00-S05-T01) (push) Successful in 3m8s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (push) Successful in 1m3s
CI / Stage 2 — Typecheck (E00-S05-T01) (push) Successful in 1m10s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (push) Successful in 56s
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (push) Successful in 45s
2026-08-31 00:34:00 +00:00
bot-implementer dbd42d394d docs(adr): record PostgreSQL 18 sole canonical DB decision as ADR-005
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Successful in 1m9s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 1m42s
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Successful in 3m13s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Successful in 1m2s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m9s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 46s
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 53s
2026-08-31 00:27:50 +00:00
kpcto 372bf1f648 Merge pull request '[E01-S01-T01] ADR: Modular monolith' (#406) from feature/188 into main
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (push) Successful in 1m11s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (push) Successful in 1m3s
CI / Stage 5 — Architecture tests (E00-S05-T01) (push) Successful in 3m14s
CI / Stage 2 — Typecheck (E00-S05-T01) (push) Successful in 1m9s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (push) Successful in 45s
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (push) Successful in 1m42s
CI / Stage 4 — Unit tests (E00-S05-T01) (push) Successful in 1m35s
2026-08-31 00:20:50 +00:00
bot-implementer 41bd4d78aa docs(adr): record modular monolith decision as ADR-001
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Successful in 3m7s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Successful in 1m3s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m12s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 45s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 1m41s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Successful in 1m8s
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 1m41s
Adds docs/adr/ADR-001-modular-monolith.md with the six required sections
(Context, Decision, Alternatives, Consequences, Operational impact, Revisit
trigger). The decision text ("Modular monolith") matches the ADR index entry
in section 70. Documentation-only; no runtime or schema impact.
2026-08-31 00:12:48 +00:00
kpcto aaa7489566 Merge pull request '[E00-S05-T01] Implement CI quality baseline (required PR stages)' (#405) from feature/187 into main
CI / Stage 2 — Typecheck (E00-S05-T01) (push) Successful in 1m9s
CI / Stage 5 — Architecture tests (E00-S05-T01) (push) Successful in 3m7s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (push) Successful in 1m2s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (push) Successful in 44s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (push) Successful in 1m8s
CI / Stage 4 — Unit tests (E00-S05-T01) (push) Successful in 1m36s
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (push) Successful in 2m32s
2026-08-31 00:07:02 +00:00
implementer 10ea1ef3db test: lock in the action SHA pins and workflow-level permissions with the ci-stages suite (E00-S05-T01)
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 4m11s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m24s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 51s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 1m49s
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Successful in 4m2s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Successful in 1m15s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Successful in 1m43s
- assertHardening: every uses: ref is a full 40-char commit SHA equal to the
  committed PINNED_ACTIONS table (no floating tags) and the workflow declares
  a top-level permissions: contents: read block
- mutation probes: reverting a pin to @v4, swapping a pinned SHA or removing
  the permissions block all fail
2026-08-30 06:29:43 +00:00
implementer d0c3b6a83d ci: pin third-party actions to full commit SHAs and declare minimal workflow permissions (E00-S05-T01)
- actions/checkout pinned to 11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2)
- actions/setup-node pinned to 1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a (v4.2.0)
- workflow-level permissions: contents: read (the pipeline only reads the repo)
- no floating @v4 tags remain anywhere in the workflow
2026-08-30 06:29:43 +00:00
implementer 13b1548df8 ci: build the config and database-postgres packages before the architecture suites (E00-S05-T01)
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 53s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m33s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 1m20s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 2m46s
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Successful in 4m55s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Successful in 1m22s
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Successful in 1m22s
The strict-tsconfig suite typechecks apps/server with tsc --noEmit, which
resolves @personal-blog/config and @personal-blog/database-postgres through
their compiled dist/ type declarations. On a fresh checkout dist/ does not
exist, so the architecture stage must build those two packages first (the
same prerequisite the unit and postgres-integration stages already declare).
2026-08-30 06:22:26 +00:00
implementer f3cd70e45d docs: document the lint command and the CI quality baseline stages (E00-S05-T01)
CI / Stage 1 — Frozen lockfile install (E00-S05-T01) (pull_request) Successful in 46s
CI / Stage 2 — Typecheck (E00-S05-T01) (pull_request) Successful in 1m15s
CI / Stage 3 — Formatting/lint policy (E00-S05-T01) (pull_request) Successful in 44s
CI / Stage 4 — Unit tests (E00-S05-T01) (pull_request) Successful in 1m44s
CI / Stage 5 — Architecture tests (E00-S05-T01) (pull_request) Failing after 56s
CI / Stage 6 — PostgreSQL integration tests (E00-S05-T01) (pull_request) Skipped
CI / Stage 7 — Build the admin and server applications (E00-S05-T01) (pull_request) Skipped
The non-container guide now covers pnpm lint (the formatting/lint policy) in
the Test and clean-clone smoke sections and describes the ordered PR CI
stages locked in by tests/ci-stages.test.mjs.
2026-08-30 06:12:25 +00:00
implementer 89fe0b52e6 ci: run the required PR quality stages in order (E00-S05-T01)
Restructure .gitea/workflows/ci.yml from a flat list of per-suite jobs into
the ordered stage baseline: frozen install -> typecheck -> formatting/lint ->
unit -> architecture -> PostgreSQL integration -> build of the admin and
server applications. Each stage gates on its predecessor through needs, so
the frozen install runs before every later stage and the pipeline halts on
the first failing stage. The docker-gated real-stack probes in the
postgres-integration (and container) suites keep running where a Docker
daemon is available and skipping cleanly otherwise.
2026-08-30 06:12:25 +00:00
implementer d9b493498e test: lock in the CI quality baseline stage order with the ci-stages suite (E00-S05-T01)
tests/ci-stages.test.mjs asserts that .gitea/workflows/ci.yml declares the
seven required PR stages (frozen-install, typecheck, formatting-lint, unit,
architecture, postgres-integration, build-apps) in order, that every later
stage gates on its predecessor through needs, that each stage runs its
expected command (frozen install, pnpm typecheck, pnpm lint, the unit /
architecture / postgres-integration node --test runs, the apps-group build
with the server artifact check), and that every committed test suite is
wired into exactly one stage. Mutation probes prove the assertions are
non-vacuous.
2026-08-30 06:12:25 +00:00
implementer e6d28f94f0 feat: add dependency-free formatting/lint policy suite and root lint script (E00-S05-T01)
The formatting-policy suite (tests/formatting-policy.test.mjs) locks in the
workspace formatting and lint policy: LF line endings, no BOM, no trailing
whitespace, no tab indentation, exactly one final newline, and valid JSON
with 2-space indentation and no duplicate keys. Every rule has a mutation
probe. The root `lint` script runs the suite; the CI formatting-lint stage
executes it.
2026-08-30 06:12:25 +00:00
bot-implementer dce6cac05b [E00-S04-T05] .env.example contains placeholders only (#404)
CI / Frozen lockfile install (push) Successful in 42s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 28s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 24s
CI / Migration ledger (E00-S03-T03) (push) Successful in 42s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 48s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 46s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 1m4s
CI / Field-specific startup errors (E00-S04-T02) (push) Successful in 1m5s
CI / Secret redaction from logs (E00-S04-T03) (push) Successful in 1m6s
CI / Env adapter owns process.env (E00-S04-T04) (push) Successful in 1m12s
CI / Compose config (E00-S03-T01) (push) Successful in 26s
CI / TypeBox/Ajv config schema (E00-S04-T01) (push) Successful in 1m4s
CI / .env.example placeholders only (E00-S04-T05) (push) Successful in 29s
Co-authored-by: bot-implementer <bot-implementer@fabrika.internal>
2026-08-30 05:47:42 +00:00
kpcto 1e0f628651 Merge pull request '[E00-S04-T04] No module reads process.env except configuration adapter' (#403) from feature/185 into main
CI / Frozen lockfile install (push) Successful in 44s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 1m4s
CI / Field-specific startup errors (E00-S04-T02) (push) Successful in 1m12s
CI / Env adapter owns process.env (E00-S04-T04) (push) Successful in 1m9s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 29s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 25s
CI / Migration ledger (E00-S03-T03) (push) Successful in 46s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 44s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 57s
CI / Secret redaction from logs (E00-S04-T03) (push) Successful in 1m4s
CI / TypeBox/Ajv config schema (E00-S04-T01) (push) Successful in 53s
CI / Compose config (E00-S03-T01) (push) Successful in 35s
2026-08-30 05:02:48 +00:00
implementer ffda249617 docs: document HOST validation and bind control (E00-S04-T04)
CI / Frozen lockfile install (pull_request) Successful in 54s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 34s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 30s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 1m16s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 54s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 46s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m6s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m2s
CI / Secret redaction from logs (E00-S04-T03) (pull_request) Successful in 1m1s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 50s
CI / Env adapter owns process.env (E00-S04-T04) (pull_request) Successful in 1m5s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 26s
The non-container guide now notes that HOST is validated at the adapter
boundary as a hostname/IP (invalid values fail startup naming the field) and
that the server passes config.host to server.listen, so a configured HOST
binds exactly that interface and the startup log reflects the actual bind.
The config-env-adapter CI job comment is refreshed to describe the extended
suite (HOST validation + loopback-only boot probe).
2026-08-30 04:42:06 +00:00
implementer 408f33e4e9 test: lock in HOST validation and bind-interface control (E00-S04-T04)
Extend the env-adapter suite to the issue's reworked acceptance criteria:
- static assertions: the adapter resolves HOST via resolveHost (hostname/IP at
  the adapter boundary) and the server passes config.host to server.listen
- deterministic boundary probe: valid HOST forms (IPv4/IPv6/hostname) pass,
  invalid HOST forms throw ConfigStartupError naming host
- boot probes: HOST=127.0.0.1 binds loopback only (no answer on a
  non-loopback interface) with the startup log reflecting the actual bind;
  an invalid HOST exits non-zero naming the field without echoing the raw
  value
- mutation probes: bypassing resolveHost or dropping config.host from
  server.listen both fail
- config-startup-error: update the order-asserion mutation probe for the new
  server.listen(config.port, config.host, ...) signature
2026-08-30 04:42:03 +00:00
implementer 345ceccfad feat: HOST validated at the adapter boundary and controls the actual bind interface (E00-S04-T04)
The environment adapter now resolves HOST through resolveHost, validating it
at the adapter boundary as a hostname (RFC 1123) or IP address (IPv4/IPv6,
node:net isIP); an invalid HOST throws a field-specific ConfigStartupError
naming host, so arbitrary env content is never used for binding or echoed
verbatim into the startup log (issue acceptance criterion, resolving security
review finding SEC-3).

The server passes config.host to server.listen(config.port, config.host, ...),
so a configured HOST binds exactly that interface and the startup log never
claims a bind the process does not enforce (resolving SEC-2).
2026-08-30 04:41:53 +00:00
implementer 73a1ae88cd chore: drop unused mkdirSync import in the env-adapter suite (E00-S04-T04)
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 55s
CI / Frozen lockfile install (pull_request) Successful in 46s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 28s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 44s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 1m5s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m32s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m41s
CI / Secret redaction from logs (E00-S04-T03) (pull_request) Successful in 1m10s
CI / Env adapter owns process.env (E00-S04-T04) (pull_request) Successful in 1m12s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 1m6s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 35s
2026-08-30 04:18:11 +00:00
implementer b9345e205c docs: document the environment adapter in the non-container guide (E00-S04-T04)
CI / Frozen lockfile install (pull_request) Successful in 46s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 24s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 32s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 50s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 48s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 45s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m20s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m20s
CI / Secret redaction from logs (E00-S04-T03) (pull_request) Successful in 1m11s
CI / Env adapter owns process.env (E00-S04-T04) (pull_request) Successful in 1m48s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 1m21s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 31s
2026-08-30 04:17:22 +00:00
implementer 3214807c9d test: update suites locked to the old direct process.env wiring for the adapter (E00-S04-T04) 2026-08-30 04:17:22 +00:00
implementer 20173a8241 test: lock in the env adapter as the single owner of process.env with static scan, mutation and deterministic probes (E00-S04-T04) 2026-08-30 04:17:22 +00:00
implementer 1214a33adb feat: server loads all settings through the config environment adapter (E00-S04-T04) 2026-08-30 04:17:19 +00:00
implementer 7aeeeaaa97 feat: environment adapter is the single owner of process.env reads (E00-S04-T04) 2026-08-30 04:17:19 +00:00
kpcto 072f5c5785 Merge pull request '[E00-S04-T03] Secrets automatically redact from logs' (#402) from feature/184 into main
CI / Frozen lockfile install (push) Successful in 49s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 24s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 48s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 35s
CI / Migration ledger (E00-S03-T03) (push) Successful in 58s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 1m8s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 1m23s
CI / Field-specific startup errors (E00-S04-T02) (push) Successful in 1m36s
CI / Secret redaction from logs (E00-S04-T03) (push) Successful in 1m17s
CI / TypeBox/Ajv config schema (E00-S04-T01) (push) Successful in 59s
CI / Compose config (E00-S03-T01) (push) Successful in 32s
2026-08-30 04:00:42 +00:00
implementer ae900589c3 docs: document the redacted resolved-configuration log in the non-container guide (E00-S04-T03)
CI / Frozen lockfile install (pull_request) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 23s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 48s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 43s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 46s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m11s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m9s
CI / Secret redaction from logs (E00-S04-T03) (pull_request) Successful in 1m18s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 57s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 26s
2026-08-30 03:54:36 +00:00
implementer 60bd097b70 test: lock in secret redaction from logs with static, mutation and deterministic probes (E00-S04-T03) 2026-08-30 03:52:55 +00:00
implementer 6458013306 feat: secrets redact from logs via config redaction layer and server redacting logger (E00-S04-T03) 2026-08-30 03:52:52 +00:00
kpcto ebb9d4f421 Merge pull request '[E00-S04-T02] Missing required setting gives field-specific startup error' (#397) from feature/183 into main
CI / Frozen lockfile install (push) Successful in 43s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 26s
CI / Migration ledger (E00-S03-T03) (push) Successful in 44s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 50s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 48s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 1m6s
CI / Field-specific startup errors (E00-S04-T02) (push) Successful in 1m18s
CI / TypeBox/Ajv config schema (E00-S04-T01) (push) Successful in 50s
CI / Compose config (E00-S03-T01) (push) Successful in 26s
2026-08-30 03:32:23 +00:00
implementer 873264004a docs: document the required EPPP_SESSION_SECRET and the startup error in the non-container guide (E00-S04-T02)
CI / Frozen lockfile install (pull_request) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 43s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 42s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m1s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 46s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m6s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 48s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 27s
2026-08-30 03:00:47 +00:00
implementer 1a9fd592d9 test: lock in the field-specific startup error with static, mutation and deterministic probes (E00-S04-T02)
- tests/config-startup-error.test.mjs: static assertions on the committed
  startup-error module, the package boundary, the server wiring (validation
  before bind), the compose secret and the Dockerfile shipping, each backed
  by mutation probes; the deterministic probes execute the issue's test plan
  ("start with a missing required field and confirm the error names it") —
  the compiled boundary throws MissingRequiredSettingError naming
  sessionSecret, and booting the committed server without EPPP_SESSION_SECRET
  exits non-zero naming the field while a valid secret boots to /health 200
- health-endpoint/app-readiness boot probes: provide a valid
  EPPP_SESSION_SECRET (the required setting is validated at startup)
- ci.yml: new config-startup-error job (builds config + database-postgres,
  runs the suite); app-readiness job now builds the config package too
- .gitignore: transient .config-startup-probe-*.mjs files
2026-08-30 03:00:45 +00:00
implementer 0ce790fca3 feat: missing required setting fails startup with a field-specific error (E00-S04-T02)
- packages/config: add src/startup.ts exposing assertValidConfig (builds on
  the T01 TypeBox/Ajv schema) and the field-specific startup errors
  (MissingRequiredSettingError names the missing field; ConfigStartupError
  names each violating field); re-export from the package boundary
- apps/server: validate the startup configuration (including the required
  EPPP_SESSION_SECRET) before the server binds, so a missing required
  setting crashes the process at startup naming the field; depends on
  @personal-blog/config
- compose.yaml: provide EPPP_SESSION_SECRET for the app service (dev-only
  >= 32 char default; override via .env / shell)
- Dockerfile: ship the compiled packages/config in the image (build source +
  runtime dist), matching the server's new workspace dependency
- pnpm-lock.yaml: apps/server importer gains @personal-blog/config
2026-08-30 03:00:40 +00:00
kpcto ecc945ce65 Merge pull request '[E00-S04-T01] TypeBox/Ajv schema' (#396) from feature/182 into main
CI / Frozen lockfile install (push) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 28s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 30s
CI / Migration ledger (E00-S03-T03) (push) Successful in 45s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 44s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 49s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 56s
CI / TypeBox/Ajv config schema (E00-S04-T01) (push) Successful in 53s
CI / Compose config (E00-S03-T01) (push) Successful in 25s
2026-08-30 02:42:55 +00:00
implementer 5eff1580ac docs: document the config package in the non-container guide (E00-S04-T01)
CI / Frozen lockfile install (pull_request) Successful in 47s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 31s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 41s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 42s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 46s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 57s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 49s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 26s
2026-08-30 02:29:52 +00:00
implementer ce0d3c0d44 test: lock in the config schema with static, mutation and deterministic probes (E00-S04-T01)
tests/config-schema.test.mjs covers both acceptance criteria: the schema
is defined with TypeBox/Ajv (static assertions on the committed package —
golden-tuple exact pins, Type.Object schema, Ajv compile, boundary
re-exports — each backed by a mutation probe proving non-vacuity) and the
schema covers the validated config fields (host, port, databaseUrl,
sessionSecret with their constraints). The deterministic probe executes
the issue's test plan — 'validate a full config against the TypeBox/Ajv
schema' — against the committed schema through Ajv via Node type
stripping (no build step), plus the negative cases (missing required field
naming sessionSecret, secret too short, unknown property, port bounds, and
empty databaseUrl); when the package is built (as in the CI job) it also
exercises the compiled validateConfig boundary exactly as the later
adapter will consume it.
2026-08-30 02:29:52 +00:00
implementer bcea489391 feat: add TypeBox/Ajv config schema package to the workspace (E00-S04-T01)
Adds packages/config (@personal-blog/config) — the EPPP configuration
service foundation. The package defines the configuration schema with
TypeBox (configSchema: host, port, databaseUrl, sessionSecret — the
validated config fields, golden-tuple pins @sinclair/typebox@0.34.52 and
ajv@8.20.0) and compiles it with Ajv (validateConfig). The environment
adapter (T04), field-specific startup errors (T02) and secret redaction
(T03) build on this boundary in later tasks; nothing reads process.env yet.

Wiring for the new workspace package: lockfile importer + resolved
typebox/ajv tree, apps/server/Dockerfile manifest copy (frozen in-image
install must match the lockfile importers), config-schema CI job, package
set fixtures (workspace-layout, workspace-config, strict-tsconfig,
typescript-pin), probe-file gitignore entry.
2026-08-30 02:29:52 +00:00
kpcto 6a65d4c789 Merge pull request '[E00-S03-T06] App does not report ready before migrations complete' (#395) from feature/181 into main
CI / Frozen lockfile install (push) Successful in 52s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 27s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 29s
CI / Migration ledger (E00-S03-T03) (push) Successful in 42s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 42s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 51s
CI / App readiness after migrations (E00-S03-T06) (push) Successful in 54s
CI / Compose config (E00-S03-T01) (push) Successful in 25s
2026-08-30 02:09:49 +00:00
implementer 5b0bded4b4 docs: document the readiness gate in the non-container guide (E00-S03-T06)
CI / Frozen lockfile install (pull_request) Successful in 49s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 23s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 40s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 56s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 41s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 55s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
2026-08-30 01:56:22 +00:00
implementer c20110be15 test: lock in the app readiness gate with static, deterministic and real-stack probes (E00-S03-T06) 2026-08-30 01:56:22 +00:00
implementer ebdee5daa5 feat: app reports ready only after the startup migration run (E00-S03-T06) 2026-08-30 01:56:19 +00:00
kpcto e8cafa090b Merge pull request '[E00-S03-T05] Migration failure produces structured diagnostic' (#394) from feature/180 into main
CI / Frozen lockfile install (push) Successful in 44s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 30s
CI / Migration ledger (E00-S03-T03) (push) Successful in 44s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 46s
CI / Migration failure diagnostic (E00-S03-T05) (push) Successful in 43s
CI / Compose config (E00-S03-T01) (push) Successful in 29s
2026-08-30 01:31:48 +00:00
implementer bb3a68648a fix: inject the ledger into MigrationRunner (type-only import) so probes load the committed module under type stripping
CI / Frozen lockfile install (pull_request) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 29s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 28s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 42s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 44s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 41s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
Node's type stripping does not rewrite './ledger.js' to './ledger.ts', so the
runner's runtime import of the ledger could not resolve when the behavioral
probes execute the committed runner.ts directly (CI failure on Node 24).
The ledger is now imported type-only and the caller passes the instance
(new MigrationLedger(pool)) — the probes already do. runner.ts has no
runtime imports left, so type stripping erases them and the committed
module loads as-is.
2026-08-30 01:24:14 +00:00
implementer 52190d082c test: lock in the migration failure diagnostic (E00-S03-T05)
CI / Frozen lockfile install (pull_request) Successful in 44s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 28s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 44s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 51s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Failing after 51s
Static assertions + mutation probes on the committed runner source, a
deterministic stub-pool behavioral probe (intentionally failing migration
fixture -> structured diagnostic naming the failing migration, apply and
record phases), a docker-gated real-stack probe against a real database
(the issue's test plan), and CI enforcement via the additive
database-postgres-diagnostic job.
2026-08-30 01:17:23 +00:00
implementer f6d407394d feat: add migration runner with structured failure diagnostic (E00-S03-T05)
MigrationRunner applies pending migrations through the migration ledger
exactly once; when a migration fails it throws a MigrationFailedError
whose diagnostic is a structured object identifying the failing migration
(version), the failure phase (apply/record), the underlying cause, and the
applied/pending ledger state, serializable via toJSON. Re-exported from
the driver boundary so no other package needs the pg driver to run
migrations. Advisory lock (T04) and ready gate (T06) remain out of scope.
2026-08-30 01:17:23 +00:00
kpcto 4f169ace4e Merge pull request '[E00-S03-T04] Advisory lock prevents concurrent migration runners' (#393) from feature/179 into main
CI / Frozen lockfile install (push) Successful in 43s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 24s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 35s
CI / Migration ledger (E00-S03-T03) (push) Successful in 41s
CI / Migration advisory lock (E00-S03-T04) (push) Successful in 43s
CI / Compose config (E00-S03-T01) (push) Successful in 29s
2026-08-30 01:00:39 +00:00
implementer cb1b161ce9 fix: destroy the connection on unlock failure so a still-locked session is never reused (E00-S03-T04)
CI / Frozen lockfile install (pull_request) Successful in 47s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 25s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 41s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 44s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
release() previously returned the connection to the pool in a finally even
when the pg_advisory_unlock statement failed, so a pooled connection could be
reused while its session still held the migration advisory lock - the next
borrower would block every other runner (reviewer finding F4). On unlock
failure the connection is now destroyed (client.release(error) removes the
client from the pool, ending the session and its lock); the plain
client.release() is kept only on the success path. Locked in by a static
assertion, a mutation probe, and a deterministic stub-pool behavioral probe
of the committed release() control flow.
2026-08-30 00:49:56 +00:00
implementer dac3679e33 fix: memoize in-flight acquire so concurrent acquire() is re-entrant-safe (E00-S03-T04)
CI / Frozen lockfile install (pull_request) Successful in 49s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 45s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 50s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
Security-review finding F2: two concurrent acquire() calls on the same
MigrationLock instance could each check out a connection; the second
pg_advisory_lock would overwrite this.client, leaking the first locked
connection until session end.

acquire() now memoizes the in-flight acquire in acquireInFlight and
returns it on re-entry, so exactly one connection is checked out and no
locked connection leaks. The memo is cleared once the acquire settles.
tryAcquire()/release() paths unchanged.

Locked in by:
- static criterion test: acquireInFlight field, re-entry guard returns
  the in-flight acquire, memo cleared on settle
- mutation probe: removing the re-entry guard fails the criterion
- real-stack probe: two concurrent acquire() calls on one instance leave
  pool.totalCount at 1 (exactly one connection), the lock granted once,
  nothing left after release; probe fails (hangs) on the pre-fix code
- CI job comment updated to reflect the re-entrancy criterion
2026-08-30 00:29:20 +00:00
implementer a2b98439e9 test: lock in the migration advisory lock with static + real-stack probes (E00-S03-T04)
CI / Frozen lockfile install (pull_request) Successful in 56s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 24s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 49s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 53s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
2026-08-30 00:13:46 +00:00
implementer 3f80063303 feat: add migration advisory lock to database-postgres (E00-S03-T04) 2026-08-30 00:08:57 +00:00
kpcto 4552ca175e Merge pull request '[E00-S03-T03] Migration ledger created' (#392) from feature/178 into main
CI / Frozen lockfile install (push) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 24s
CI / Migration ledger (E00-S03-T03) (push) Successful in 49s
CI / Compose config (E00-S03-T01) (push) Successful in 24s
2026-08-29 23:37:16 +00:00
implementer e52b19b1e7 test: lock in the migration ledger with static + real-stack probes (E00-S03-T03)
CI / Frozen lockfile install (pull_request) Successful in 56s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 28s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 42s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 29s
Static assertions on the committed ledger source (idempotent table DDL,
parameterized idempotent record, has/applied queries, driver-boundary
re-export, CI enforcement) with mutation probes proving non-vacuousness;
docker-gated real-stack probe migrates an empty database (isolated compose
project + host port) and confirms the ledger exists, the applied migrations
are recorded, and a re-run records nothing twice. New additive
database-postgres-ledger CI job gates the criterion on every PR.
2026-08-29 23:20:00 +00:00