[E00-S05-T01] Implement CI quality baseline (required PR stages) #405

Merged
kpcto merged 7 commits from feature/187 into main 2026-08-31 00:07:03 +00:00
Member

What changed

  • CI (.gitea/workflows/ci.yml) — restructured from a flat list of per-suite jobs into the seven ordered PR stages of the quality baseline, each gated on its predecessor via needs so the pipeline runs in order and halts on the first failing stage:
    1. frozen-install — pnpm install --frozen-lockfile (runs before every later stage)
    2. typecheck — pnpm typecheck (every workspace package passes tsc --noEmit)
    3. formatting-lint — pnpm lint (new dependency-free formatting/lint policy)
    4. unit — health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter, env-example suites
    5. architecture — dependency-boundary, workspace layout/config, strict-tsconfig, engine/TypeScript pins, root-commands, frozen-install clean clone, container structure (compose-config, build-targets, non-root-user, readonly-rootfs), database-postgres-imports and the new ci-stages suites
    6. postgres-integration — database-postgres-ledger/lock/diagnostic and app-readiness suites (docker-gated real-stack probes run where a Docker daemon is available and skip cleanly otherwise)
    7. build-apps — builds the apps group (pnpm --filter "./apps/**" run build — apps/server today, apps/admin when E06-S01 lands) and verifies the compiled apps/server/dist/index.js artifact
  • Third-party action hardening — actions/checkout is pinned to 11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) and actions/setup-node to 1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a (v4.2.0), both full commit SHAs — no floating @v4 tags anywhere in the workflow.
  • Minimal workflow permissions — the workflow now declares permissions: contents: read at the top level: the pipeline only reads repository contents, so the default (write) token is never needed.
  • Formatting/lint policy — tests/formatting-policy.test.mjs (new, dependency-free): every tracked text file must use LF endings, no BOM, no trailing whitespace, no tab indentation and exactly one final newline; JSON files must parse, carry no duplicate keys and use 2-space indentation. Every rule has a mutation probe.
  • Root script — package.json gains "lint": "node --test tests/formatting-policy.test.mjs".
  • Baseline lock — tests/ci-stages.test.mjs (new, dependency-free) locks the stage order, the needs chain, the per-stage commands, the action SHA pins, the workflow-level permissions: contents: read block and the invariant that every committed tests/*.test.mjs suite is wired into exactly one CI stage (mutation probes: removing a stage, breaking the chain, dropping/reordering a suite, reverting a pin to a tag, changing a pin or removing the permissions block all fail).
  • Architecture-stage fix — the stage builds the config and database-postgres packages before the strict-tsconfig suite: apps/server imports their compiled type declarations (dist/index.d.ts), which tsc -p apps/server/tsconfig.json --noEmit cannot resolve on a clean checkout otherwise (this suite was not executed by the previous workflow and surfaced on first CI run).
  • Docs — docs/development/non-container.md documents pnpm lint and the ordered PR CI stages.
  • Existing per-suite CI-gating assertions (the node --test tests/<suite>.test.mjs includes-checks in the config/database/app-readiness suites, the config build-step checks, node-version: '24', corepack enable, --frozen-lockfile) all still hold against the restructured workflow — verified by the full local suite run (296 tests: 283 pass, 13 docker-gated real-stack probes skip cleanly, 0 fail) on Node 24 with the exact CI commands.

Criterion → test mapping

Criterion (issue #187) Test
CI runs frozen install before later stages ci-stages: "the committed CI workflow runs the required PR stages in order" — frozen-install is the first job and every later stage declares needs on its predecessor, so the serial chain runs the frozen install before anything else; every stage job also installs with pnpm install --frozen-lockfile. Backed by the frozen-install stage job and the frozen-install clean-clone suite (architecture stage)
CI runs typecheck ci-stages: the typecheck stage must run pnpm typecheck (asserted); the stage job executes pnpm typecheck — all five workspace packages pass tsc --noEmit (verified locally, exit 0)
CI runs formatting/lint ci-stages: the formatting-lint stage must run pnpm lint, and the root lint script must equal node --test tests/formatting-policy.test.mjs (both asserted); the formatting-policy suite enforces the policy on every tracked file with mutation probes proving each rule is non-vacuous
CI runs unit tests ci-stages: the unit stage names the health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter and env-example suites; each suite's own per-PR CI-gating assertion (node --test tests/<suite>.test.mjs present in the workflow) still passes
CI runs architecture tests ci-stages: the architecture stage names the dependency-boundary (architecture-import, no-core-extension-imports, database-postgres-imports), workspace (layout, config, strict-tsconfig, typescript-pin, node-engine, root-commands, frozen-install) and container-structure (compose-config, build-targets, non-root-user, readonly-rootfs) suites plus ci-stages itself
CI runs PostgreSQL integration tests ci-stages: the postgres-integration stage names database-postgres-ledger/lock/diagnostic and app-readiness; their docker-gated real-stack probes (migrate an empty database, advisory-lock concurrency, readiness waits on the migration run) run where a Docker daemon is available and skip cleanly otherwise, while the static and deterministic probes always gate
CI builds the admin and server applications ci-stages: the build-apps stage must build the apps group (./apps/**) and verify apps/server/dist/index.js (asserted); verified locally: pnpm --filter "./apps/**" run build emits the server artifact, and the apps-group glob picks up apps/admin automatically when E06-S01 lands
CI workflow pins third-party actions (actions/checkout, actions/setup-node) to full commit SHAs, not floating tags ci-stages: "the workflow pins third-party actions to full commit SHAs and declares minimal permissions" — every uses: ref must match <owner>/<repo>@<40-hex-SHA> and equal the committed PINNED_ACTIONS table (actions/checkout@11bd7190… v4.2.2, actions/setup-node@1d0ff469… v4.2.0). Mutation probes: reverting a pin to @v4 or swapping the SHA fails the assertion
CI workflow declares minimal permissions (permissions: contents: read) at the workflow level ci-stages: the same hardening test asserts the top-level permissions: contents: read block (regex-locked to the workflow level). Mutation probe: removing the block fails the assertion
CI workflow change requires a human maintainer approval on the PR before merge (review-checklist §6.4 pipeline tripwire) Process gate, not a code test: this PR changes .gitea/workflows/ci.yml, so per review-checklist §6.4 the change is a blocker for any automated approver and requires an explicit human maintainer review/approval before merge (security finding S1 on this PR)
every committed test suite gates PRs ci-stages: coverage assertion — the union of the unit/architecture/postgres-integration node --test runs plus pnpm lint names every tests/*.test.mjs file exactly once (mutation probe: dropping a suite from its stage fails)

Risks

  • Pipeline tripwire (review checklist §6.4, security finding S1 — issue #186 Notes): this PR changes .gitea/workflows/ci.yml; a human maintainer decision/approval is required on the PR, regardless of author. No code change beyond that sign-off.
  • No new dependencies: the two new suites are node:test-only and the lockfile is untouched (pnpm install --frozen-lockfile unchanged).
  • The docker-gated real-stack probes in the postgres-integration and container suites remain skip-cleanly-without-a-daemon (the current runner has no Docker daemon; they ran as 13 skips locally, matching the previous CI behavior).
  • Action pins: the pinned SHAs resolve to the released actions/checkout@v4.2.2 and actions/setup-node@v4.2.0 commits; bumping an action means updating both the workflow and the PINNED_ACTIONS table in tests/ci-stages.test.mjs in the same PR.
  • CI wall-clock increases: typecheck, lint, architecture and build are new stages — that is the intended quality baseline, not a regression.
  • The admin application does not exist yet (E06-S01); the build stage is scoped to the apps group so it is built automatically once apps/admin lands.
  • Out of scope per issue: container/Compose smoke on main/release branches, the Docker Compose baseline stack (E00-S02) and configuration service validation (E00-S04).
## What changed - **CI** (`.gitea/workflows/ci.yml`) — restructured from a flat list of per-suite jobs into the **seven ordered PR stages** of the quality baseline, each gated on its predecessor via `needs` so the pipeline runs in order and halts on the first failing stage: 1. `frozen-install` — `pnpm install --frozen-lockfile` (runs before every later stage) 2. `typecheck` — `pnpm typecheck` (every workspace package passes `tsc --noEmit`) 3. `formatting-lint` — `pnpm lint` (new dependency-free formatting/lint policy) 4. `unit` — health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter, env-example suites 5. `architecture` — dependency-boundary, workspace layout/config, strict-tsconfig, engine/TypeScript pins, root-commands, frozen-install clean clone, container structure (compose-config, build-targets, non-root-user, readonly-rootfs), database-postgres-imports and the new ci-stages suites 6. `postgres-integration` — database-postgres-ledger/lock/diagnostic and app-readiness suites (docker-gated real-stack probes run where a Docker daemon is available and skip cleanly otherwise) 7. `build-apps` — builds the apps group (`pnpm --filter "./apps/**" run build` — apps/server today, apps/admin when E06-S01 lands) and verifies the compiled `apps/server/dist/index.js` artifact - **Third-party action hardening** — `actions/checkout` is pinned to `11bd71901bbe5b1630ceea73d27597364c9af683` (v4.2.2) and `actions/setup-node` to `1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a` (v4.2.0), both full commit SHAs — no floating `@v4` tags anywhere in the workflow. - **Minimal workflow permissions** — the workflow now declares `permissions: contents: read` at the top level: the pipeline only reads repository contents, so the default (write) token is never needed. - **Formatting/lint policy** — `tests/formatting-policy.test.mjs` (new, dependency-free): every tracked text file must use LF endings, no BOM, no trailing whitespace, no tab indentation and exactly one final newline; JSON files must parse, carry no duplicate keys and use 2-space indentation. Every rule has a mutation probe. - **Root script** — `package.json` gains `"lint": "node --test tests/formatting-policy.test.mjs"`. - **Baseline lock** — `tests/ci-stages.test.mjs` (new, dependency-free) locks the stage order, the `needs` chain, the per-stage commands, the action SHA pins, the workflow-level `permissions: contents: read` block and the invariant that every committed `tests/*.test.mjs` suite is wired into exactly one CI stage (mutation probes: removing a stage, breaking the chain, dropping/reordering a suite, reverting a pin to a tag, changing a pin or removing the permissions block all fail). - **Architecture-stage fix** — the stage builds the `config` and `database-postgres` packages before the `strict-tsconfig` suite: `apps/server` imports their compiled type declarations (`dist/index.d.ts`), which `tsc -p apps/server/tsconfig.json --noEmit` cannot resolve on a clean checkout otherwise (this suite was not executed by the previous workflow and surfaced on first CI run). - **Docs** — `docs/development/non-container.md` documents `pnpm lint` and the ordered PR CI stages. - Existing per-suite CI-gating assertions (the `node --test tests/<suite>.test.mjs` includes-checks in the config/database/app-readiness suites, the config build-step checks, `node-version: '24'`, `corepack enable`, `--frozen-lockfile`) all still hold against the restructured workflow — verified by the full local suite run (296 tests: 283 pass, 13 docker-gated real-stack probes skip cleanly, 0 fail) on Node 24 with the exact CI commands. ## Criterion → test mapping | Criterion (issue #187) | Test | | --- | --- | | CI runs frozen install before later stages | `ci-stages`: "the committed CI workflow runs the required PR stages in order" — `frozen-install` is the first job and every later stage declares `needs` on its predecessor, so the serial chain runs the frozen install before anything else; every stage job also installs with `pnpm install --frozen-lockfile`. Backed by the `frozen-install` stage job and the `frozen-install` clean-clone suite (architecture stage) | | CI runs typecheck | `ci-stages`: the `typecheck` stage must run `pnpm typecheck` (asserted); the stage job executes `pnpm typecheck` — all five workspace packages pass `tsc --noEmit` (verified locally, exit 0) | | CI runs formatting/lint | `ci-stages`: the `formatting-lint` stage must run `pnpm lint`, and the root `lint` script must equal `node --test tests/formatting-policy.test.mjs` (both asserted); the `formatting-policy` suite enforces the policy on every tracked file with mutation probes proving each rule is non-vacuous | | CI runs unit tests | `ci-stages`: the `unit` stage names the health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter and env-example suites; each suite's own per-PR CI-gating assertion (`node --test tests/<suite>.test.mjs` present in the workflow) still passes | | CI runs architecture tests | `ci-stages`: the `architecture` stage names the dependency-boundary (architecture-import, no-core-extension-imports, database-postgres-imports), workspace (layout, config, strict-tsconfig, typescript-pin, node-engine, root-commands, frozen-install) and container-structure (compose-config, build-targets, non-root-user, readonly-rootfs) suites plus ci-stages itself | | CI runs PostgreSQL integration tests | `ci-stages`: the `postgres-integration` stage names database-postgres-ledger/lock/diagnostic and app-readiness; their docker-gated real-stack probes (migrate an empty database, advisory-lock concurrency, readiness waits on the migration run) run where a Docker daemon is available and skip cleanly otherwise, while the static and deterministic probes always gate | | CI builds the admin and server applications | `ci-stages`: the `build-apps` stage must build the apps group (`./apps/**`) and verify `apps/server/dist/index.js` (asserted); verified locally: `pnpm --filter "./apps/**" run build` emits the server artifact, and the apps-group glob picks up `apps/admin` automatically when E06-S01 lands | | CI workflow pins third-party actions (actions/checkout, actions/setup-node) to full commit SHAs, not floating tags | `ci-stages`: "the workflow pins third-party actions to full commit SHAs and declares minimal permissions" — every `uses:` ref must match `<owner>/<repo>@<40-hex-SHA>` and equal the committed `PINNED_ACTIONS` table (`actions/checkout@11bd7190…` v4.2.2, `actions/setup-node@1d0ff469…` v4.2.0). Mutation probes: reverting a pin to `@v4` or swapping the SHA fails the assertion | | CI workflow declares minimal permissions (`permissions: contents: read`) at the workflow level | `ci-stages`: the same hardening test asserts the top-level `permissions: contents: read` block (regex-locked to the workflow level). Mutation probe: removing the block fails the assertion | | CI workflow change requires a human maintainer approval on the PR before merge (review-checklist §6.4 pipeline tripwire) | Process gate, not a code test: this PR changes `.gitea/workflows/ci.yml`, so per review-checklist §6.4 the change is a blocker for any automated approver and requires an explicit human maintainer review/approval before merge (security finding S1 on this PR) | | every committed test suite gates PRs | `ci-stages`: coverage assertion — the union of the unit/architecture/postgres-integration `node --test` runs plus `pnpm lint` names every `tests/*.test.mjs` file exactly once (mutation probe: dropping a suite from its stage fails) | ## Risks - **Pipeline tripwire** (review checklist §6.4, security finding S1 — issue #186 Notes): this PR changes `.gitea/workflows/ci.yml`; a human maintainer decision/approval is required on the PR, regardless of author. No code change beyond that sign-off. - No new dependencies: the two new suites are `node:test`-only and the lockfile is untouched (`pnpm install --frozen-lockfile` unchanged). - The docker-gated real-stack probes in the postgres-integration and container suites remain skip-cleanly-without-a-daemon (the current runner has no Docker daemon; they ran as 13 skips locally, matching the previous CI behavior). - Action pins: the pinned SHAs resolve to the released `actions/checkout@v4.2.2` and `actions/setup-node@v4.2.0` commits; bumping an action means updating both the workflow and the `PINNED_ACTIONS` table in `tests/ci-stages.test.mjs` in the same PR. - CI wall-clock increases: typecheck, lint, architecture and build are new stages — that is the intended quality baseline, not a regression. - The admin application does not exist yet (E06-S01); the build stage is scoped to the apps group so it is built automatically once `apps/admin` lands. - Out of scope per issue: container/Compose smoke on main/release branches, the Docker Compose baseline stack (E00-S02) and configuration service validation (E00-S04).
bot-implementer added 4 commits 2026-08-30 06:12:49 +00:00
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.
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.
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.
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
f3cd70e45d
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.
bot-implementer added 1 commit 2026-08-30 06:22:30 +00:00
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
13b1548df8
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).
kpcto approved these changes 2026-08-30 06:31:35 +00:00
Dismissed
kpcto left a comment
Owner

Human-maintainer approval (kpcto) of the .gitea/workflows/ci.yml change on PR #405 (pipeline tripwire, review-checklist §6.4): the 7-stage quality baseline (frozen-install → typecheck → formatting-lint → unit → architecture → postgres-integration → build-apps) is intended and in-scope, it alters no existing gating step in a weakening direction, and the additive env-example-style hardening (SHA pinning + permissions: contents: read) is required by the reworked acceptance criteria. I approve the workflow change subject to those F2/F3 hardenings landing.

Human-maintainer approval (kpcto) of the `.gitea/workflows/ci.yml` change on PR #405 (pipeline tripwire, review-checklist §6.4): the 7-stage quality baseline (frozen-install → typecheck → formatting-lint → unit → architecture → postgres-integration → build-apps) is intended and in-scope, it alters no existing gating step in a weakening direction, and the additive `env-example`-style hardening (SHA pinning + `permissions: contents: read`) is required by the reworked acceptance criteria. I approve the workflow change subject to those F2/F3 hardenings landing.
bot-implementer added 2 commits 2026-08-30 06:32:54 +00:00
- 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
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
10ea1ef3db
- 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
kpcto approved these changes 2026-08-30 06:40:07 +00:00
kpcto left a comment
Owner

Human-maintainer approval (kpcto) on the final head 10ea1ef3: I approve the .gitea/workflows/ci.yml change (7-stage quality baseline, SHA-pinned actions, permissions: contents: read) — this refreshes my earlier approval so it is non-stale on the current head.

Human-maintainer approval (kpcto) on the final head 10ea1ef3: I approve the `.gitea/workflows/ci.yml` change (7-stage quality baseline, SHA-pinned actions, `permissions: contents: read`) — this refreshes my earlier approval so it is non-stale on the current head.
Member
agent: tester
issue: 187
pr: 405
phase: start
status: in_progress
objective: independently probe the PR for #187
scope_files: []
constraints:
  - never edit the implementer's tests in the same PR
method:
  - read PR diff, workflow file, and CI runs via Gitea APIs
  - clone feature/187 and run the two new dependency-free suites locally
  - verify each acceptance criterion against evidence, not the PR description
started_at: 2026-08-30T06:56:11Z
```yaml agent: tester issue: 187 pr: 405 phase: start status: in_progress objective: independently probe the PR for #187 scope_files: [] constraints: - never edit the implementer's tests in the same PR method: - read PR diff, workflow file, and CI runs via Gitea APIs - clone feature/187 and run the two new dependency-free suites locally - verify each acceptance criterion against evidence, not the PR description started_at: 2026-08-30T06:56:11Z ```
Member
agent: tester
issue: 187
pr: 405
phase: end
status: complete
head_sha_probed: 10ea1ef3db678f606ae42c1848336bd174fc031c
verdict: pass
acceptance:
  - criterion: CI runs frozen install before later stages
    result: pass
    evidence:
      - ".gitea/workflows/ci.yml: `frozen-install` is the first job (runs `pnpm install --frozen-lockfile`)"
      - "needs chain (grep): typecheck→frozen-install, formatting-lint→typecheck, unit→formatting-lint, architecture→unit, postgres-integration→architecture, build-apps→postgres-integration"
      - "CI job 356 (Stage 1 — Frozen lockfile install) success"
  - criterion: CI runs typecheck, formatting/lint, unit, architecture and PostgreSQL integration tests
    result: pass
    evidence:
      - "typecheck: `pnpm typecheck` — job 357 success"
      - "formatting-lint: `pnpm lint` → tests/formatting-policy.test.mjs — job 358 success"
      - "unit: health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter, env-example — job 359 success"
      - "architecture: architecture-import, no-core-extension-imports, workspace-layout, workspace-config, strict-tsconfig, typescript-pin, node-engine, frozen-install, root-commands, compose-config, build-targets, non-root-user, readonly-rootfs, database-postgres-imports, ci-stages — job 360 success"
      - "postgres-integration: database-postgres-ledger/lock/diagnostic, app-readiness — job 361 success"
  - criterion: CI builds the admin and server applications
    result: pass
    evidence:
      - "build-apps: `pnpm --filter \"./apps/**\" run build` + `test -f apps/server/dist/index.js` — job 362 success"
    note: "apps/admin does not yet exist in the tree (E06-S01, out of scope for this issue); the apps-group glob will pick it up automatically once it lands. Server artifact verified."
  - criterion: CI workflow pins third-party actions (actions/checkout, actions/setup-node) to full commit SHAs, not floating tags
    result: pass
    evidence:
      - "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 (14 occurrences)"
      - "actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 (14 occurrences)"
      - "grep 'uses: actions/.*@v' → no matches"
      - "CI checkout step names render the pinned SHA, confirming the SHA is what actually executes"
  - criterion: CI workflow declares minimal permissions (`permissions: contents: read`) at the workflow level
    result: pass
    evidence:
      - ".gitea/workflows/ci.yml lines 11-12: top-level `permissions:` block with `contents: read`, declared before `jobs:`; no job declares any additional scope"
  - criterion: CI workflow change requires a human maintainer approval on the PR before merge (review-checklist §6.4 pipeline tripwire)
    result: pass
    evidence:
      - "review id 2 by kpcto (human maintainer): state APPROVED, official=true, stale=false, on final head 10ea1ef3db678f606ae42c1848336bd174fc031c (submitted 2026-08-30T06:40:07Z)"
      - "earlier review id 1 (kpcto) is dismissed=true/stale=true, superseded by the non-stale approval above"
independent_probe:
  - "cloned feature/187 @ 10ea1ef3 and inspected .gitea/workflows/ci.yml directly (grep of uses:/permissions:/needs:/job keys)"
  - "local `node --test tests/ci-stages.test.mjs` → 11 pass, 0 fail"
  - "local `node --test tests/formatting-policy.test.mjs` → 13 pass, 0 fail"
constraint_respected:
  - "never edit the implementer's tests in the same PR — no file was modified"
ci_links:
  run: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112
  jobs:
    stage_1_frozen_install: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/356
    stage_2_typecheck: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/357
    stage_3_formatting_lint: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/358
    stage_4_unit: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/359
    stage_5_architecture: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/360
    stage_6_postgres_integration: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/361
    stage_7_build_apps: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/362
finished_at: 2026-08-30T06:56:11Z
```yaml agent: tester issue: 187 pr: 405 phase: end status: complete head_sha_probed: 10ea1ef3db678f606ae42c1848336bd174fc031c verdict: pass acceptance: - criterion: CI runs frozen install before later stages result: pass evidence: - ".gitea/workflows/ci.yml: `frozen-install` is the first job (runs `pnpm install --frozen-lockfile`)" - "needs chain (grep): typecheck→frozen-install, formatting-lint→typecheck, unit→formatting-lint, architecture→unit, postgres-integration→architecture, build-apps→postgres-integration" - "CI job 356 (Stage 1 — Frozen lockfile install) success" - criterion: CI runs typecheck, formatting/lint, unit, architecture and PostgreSQL integration tests result: pass evidence: - "typecheck: `pnpm typecheck` — job 357 success" - "formatting-lint: `pnpm lint` → tests/formatting-policy.test.mjs — job 358 success" - "unit: health-endpoint, secrets-not-embedded, config-schema, config-startup-error, config-log-redaction, config-env-adapter, env-example — job 359 success" - "architecture: architecture-import, no-core-extension-imports, workspace-layout, workspace-config, strict-tsconfig, typescript-pin, node-engine, frozen-install, root-commands, compose-config, build-targets, non-root-user, readonly-rootfs, database-postgres-imports, ci-stages — job 360 success" - "postgres-integration: database-postgres-ledger/lock/diagnostic, app-readiness — job 361 success" - criterion: CI builds the admin and server applications result: pass evidence: - "build-apps: `pnpm --filter \"./apps/**\" run build` + `test -f apps/server/dist/index.js` — job 362 success" note: "apps/admin does not yet exist in the tree (E06-S01, out of scope for this issue); the apps-group glob will pick it up automatically once it lands. Server artifact verified." - criterion: CI workflow pins third-party actions (actions/checkout, actions/setup-node) to full commit SHAs, not floating tags result: pass evidence: - "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 (14 occurrences)" - "actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 (14 occurrences)" - "grep 'uses: actions/.*@v' → no matches" - "CI checkout step names render the pinned SHA, confirming the SHA is what actually executes" - criterion: CI workflow declares minimal permissions (`permissions: contents: read`) at the workflow level result: pass evidence: - ".gitea/workflows/ci.yml lines 11-12: top-level `permissions:` block with `contents: read`, declared before `jobs:`; no job declares any additional scope" - criterion: CI workflow change requires a human maintainer approval on the PR before merge (review-checklist §6.4 pipeline tripwire) result: pass evidence: - "review id 2 by kpcto (human maintainer): state APPROVED, official=true, stale=false, on final head 10ea1ef3db678f606ae42c1848336bd174fc031c (submitted 2026-08-30T06:40:07Z)" - "earlier review id 1 (kpcto) is dismissed=true/stale=true, superseded by the non-stale approval above" independent_probe: - "cloned feature/187 @ 10ea1ef3 and inspected .gitea/workflows/ci.yml directly (grep of uses:/permissions:/needs:/job keys)" - "local `node --test tests/ci-stages.test.mjs` → 11 pass, 0 fail" - "local `node --test tests/formatting-policy.test.mjs` → 13 pass, 0 fail" constraint_respected: - "never edit the implementer's tests in the same PR — no file was modified" ci_links: run: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112 jobs: stage_1_frozen_install: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/356 stage_2_typecheck: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/357 stage_3_formatting_lint: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/358 stage_4_unit: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/359 stage_5_architecture: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/360 stage_6_postgres_integration: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/361 stage_7_build_apps: http://gitea:3000/Fabrika/PersonalBlog/actions/runs/112/jobs/362 finished_at: 2026-08-30T06:56:11Z ```
kpcto merged commit aaa7489566 into main 2026-08-31 00:07:03 +00:00
kpcto deleted branch feature/187 2026-08-31 00:07:04 +00:00
Sign in to join this conversation.