Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
bcb7500
docs(dev): add the build-gate discipline page and index it
cuioss-oliver Aug 9, 2026
c5253a0
docs(dev): state the declared-versus-asserted rules behind the matrix
cuioss-oliver Aug 9, 2026
8f684c5
docs(dev): record that the plan's footprint governs in both directions
cuioss-oliver Aug 9, 2026
d593fb8
docs(demo-client): complete the browser-policy class and add the owed…
cuioss-oliver Aug 9, 2026
77f7b21
docs: land three always-on statements in CLAUDE.md
cuioss-oliver Aug 9, 2026
dea85c5
ci: report the OpenRewrite dirty tree without gating on it
cuioss-oliver Aug 9, 2026
2ad0e52
docs(dev): restate the fixed point as observed state plus drift risk
cuioss-oliver Aug 9, 2026
62b3f24
ci: correct the rewrite-report job's non-gating rationale
cuioss-oliver Aug 9, 2026
bdb1346
chore(simplify): collapse duplicated rationale and fix a stale count
cuioss-oliver Aug 9, 2026
120a2d8
fix(ci): invoke the rewriting gate by coordinates, not by goal prefix
cuioss-oliver Aug 9, 2026
58294ab
ci(rewrite-report): keep the gate log out of the tree report it feeds
cuioss-oliver Aug 9, 2026
c51f33e
docs(gate): deletion test proves coverage, not runtime effect
cuioss-oliver Aug 9, 2026
c23b926
docs(demo-client): the Playwright suite is coverage, not a gate
cuioss-oliver Aug 9, 2026
dfa332a
docs(sonar): scope the new-code claim to changed lines, name the disp…
cuioss-oliver Aug 9, 2026
e03b6a6
chore(simplify): drop a line-number anchor from the rewrite-report co…
cuioss-oliver Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/skills/run-integration-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ Use a 10-minute Bash timeout. On a loaded machine the whole cycle can exceed the
```
3. Curl the health endpoints (below) and read `docker compose logs api-sheriff`. Re-run only step 3 while iterating.

## Two pointers out of this stack

- **A green run here is not evidence about browser-enforced controls.** These suites drive the gateway with RestAssured. `demo-client/doc/playwright-suite.adoc` states why that leaves a whole class of controls outside their reach, enumerates the class, and carries the Playwright suite that is the only coverage in this repository for it — a suite that gates nothing, since it runs on pushes to `main` and on demand but never on a pull request. Read it before concluding anything about that class from a green run here.
- **Need a subset of this stack rather than all of it?** `demo-client/scripts/start-dev-environment.sh` already carries the working invocation for a trimmed three-container bring-up. Do not reach for a Compose profile instead: the standing prohibition, with its reasoning, is in `demo-client/doc/playwright-suite.adoc`.

## Port & health map

| Service | Container | Host | Notes |
Expand Down
132 changes: 132 additions & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,135 @@ jobs:
trivy-filesystem.sarif
retention-days: 30
if-no-files-found: error

# OPENREWRITE DIRTY-TREE REPORT — deliberately NON-GATING.
#
# Three mechanisms in this repository mutate the working tree while the gate exits 0, and all three
# are invisible in the build result by construction. They are documented in
# doc/development/build-gate-discipline.adoc; this job is the only part of that story a machine can
# carry, so it PRINTS what the rewriting gate changed and which recipe changed it, and never judges.
#
# `-Ppre-commit` IS LOAD-BEARING. The recipe list that reaches all three mechanisms is the
# pre-commit profile's `activeRecipes` override in the root pom.xml. Without the profile,
# `rewrite:run` reports `Using active recipe(s) []`, changes nothing, and this job becomes a false
# green that appears to exonerate OpenRewrite. Do not "simplify" the profile away.
#
# No CI lane runs the rewriting gate today, so this job has to invoke it before it has a dirty tree
# to report. That is CI time this repository did not previously spend; the invocation is kept to the
# narrowest form that still produces all three mutations.
#
# DELIBERATE DIVERGENCE FROM ADR-0030, which holds that a repository invariant asserted only by an
# explanatory comment becomes a positively-phrased, machine-checked fitness function. This lane is
# deliberately NOT that shape, because the fixed point is observed rather than held. The argument,
# and the condition under which ADR-0030's shape becomes the right call, are in
# doc/development/build-gate-discipline.adoc under "The enforcement" — read it before converting
# this job into a hard fail-on-dirty gate.
#
# No `needs:` — patterned on supply-chain-scan above; the job neither delays nor is delayed by
# `build`. It commits nothing and pushes nothing: the mutated tree exists only inside the runner.
rewrite-report:
name: OpenRewrite dirty-tree report (non-gating)
runs-on: ubuntu-latest
timeout-minutes: 30
Comment thread
coderabbitai[bot] marked this conversation as resolved.
permissions:
contents: read

steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up JDK 25
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
java-version: '25'
distribution: 'temurin'
cache: maven

# `shell: bash` is required, not decorative: it selects `-eo pipefail`, whereas the default
# `bash -e` leaves pipefail OFF and the `| tee` below would swallow a Maven failure. A genuine
# gate or tooling failure must still turn this check red — only the DIRTY VERDICT is non-gating,
# which is why `continue-on-error` is deliberately not used on this job or any of its steps.
# The goal is invoked by FULL COORDINATES, not by the `rewrite:` prefix. A prefix has to be
# resolved through `org.openrewrite.maven/maven-metadata.xml` on Central, which is a network
# fetch this job does not otherwise need — and when Central answers 429 the prefix does not
# resolve, the gate never runs, and the job dies with `No plugin found for prefix 'rewrite'`.
# Full coordinates resolve the version from the pre-commit profile's own plugin declaration.
# The log goes to `.plan/temp/`, which `.gitignore` already excludes, NOT to the checkout
# root. A log in the root is picked up by the `git status --porcelain` below as `?? rewrite-run.log`
# — the job would manufacture one tree mutation of its own and report it next to the real ones,
# leaving a reader unable to tell the artefact from the finding.
- name: Run the rewriting gate
shell: bash
run: |
mkdir -p .plan/temp
./mvnw -B -ntp -Ppre-commit org.openrewrite.maven:rewrite-maven-plugin:run -DskipTests 2>&1 | tee .plan/temp/rewrite-run.log

# Report, without a verdict. Nothing here asserts cleanliness, so there is no `exit 1` path:
# a dirty tree produces a longer summary and a green check, which is the whole point.
#
# The attribution parser is coupled to OpenRewrite's log wording and to its indentation, and
# that coupling cannot be removed short of reimplementing the plugin's reporting. What CAN be
# removed is the SILENCE. When the wording or the indentation moves, the matcher stops matching
# and an empty attribution block reads exactly like "no recipe changed anything" — a confident
# report of nothing having happened. So the match counts are cross-checked against the working
# tree: a dirty tree with zero matched headers, or headers with no recipe lines beneath them,
# is reported as a BROKEN PARSER and never as a clean run.
- name: Publish the dirty-tree report to the job summary
shell: bash
run: |
tree_status="$(git status --porcelain)"
counts="$(awk -v out=.plan/temp/attribution.txt '
/Changes have been made to / { block = 1; headers++; print > out; next }
block && /^\[INFO\][[:space:]][[:space:]]+/ { detail++; print > out; next }
block && /^[[:space:]][[:space:]]+/ { detail++; print > out; next }
{ block = 0 }
END { print headers + 0, detail + 0 }
' .plan/temp/rewrite-run.log)"
headers="${counts% *}"
detail="${counts#* }"
{
echo "## OpenRewrite dirty-tree report (non-gating)"
echo
echo "The findings below NEVER fail this check. They record what the rewriting gate"
echo "changed in the runner's working tree while exiting 0. See"
echo "\`doc/development/build-gate-discipline.adoc\` for what to do about them."
echo
echo "### Working tree after the gate"
echo '```'
if [ -n "$tree_status" ]; then
printf '%s\n' "$tree_status"
else
echo "(clean - the gate rewrote nothing)"
fi
echo '```'
echo
echo "### Recipe attribution"
echo '```'
if [ "$headers" -gt 0 ]; then
cat .plan/temp/attribution.txt
else
echo "(no recipe reported a change)"
fi
echo '```'
if [ "$headers" -eq 0 ] && [ -n "$tree_status" ]; then
echo
echo "> **Parser drift, not a clean run.** The tree above is dirty, yet no"
echo "> \`Changes have been made to\` line matched. Do NOT read the empty attribution as"
echo "> \"no recipe ran\" — OpenRewrite's log wording has most likely moved. Attribute the"
echo "> changes from the raw gate log in the previous step, and fix the awk matcher in"
echo "> \`.github/workflows/maven.yml\`."
elif [ "$headers" -gt 0 ] && [ "$detail" -eq 0 ]; then
echo
echo "> **Parser drift, partial match.** $headers change header(s) matched but no recipe"
echo "> line beneath them did, so the attribution above names no recipe. OpenRewrite's log"
echo "> indentation has most likely moved; fix the awk matcher in"
echo "> \`.github/workflows/maven.yml\`."
fi
} >> "$GITHUB_STEP_SUMMARY"
12 changes: 12 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ turned on over was retired. A `@SuppressWarnings` added to get back to green hol
while leaving it reporting success, which is worse than not having it — and it collides with the
Pre-1.0 rule below that forbids carrying deprecated code at all.

**A successful build is not evidence that work happened.** `BUILD SUCCESS` says the build
completed — not that it compiled what you changed, ran what you wrote, or kept what you fixed. The
gate below and every specific case documented off it are instances of that one rule.

**A gate that exits 0 can still have changed your files**, and three different mechanisms in this
repository do. So a review-bot suggestion is verified by *surviving* the gate, never by being
implemented: run the gate, then `git status --porcelain`, and attribute a dirty tree before
reverting it. Revert unrelated churn; keep the rewrite only for files the branch itself authored.
See `doc/development/build-gate-discipline.adoc` for the three mechanisms, the three operational
consequences and the recipe-scoping trap.

**Documentation-only commits skip both.** A commit whose entire footprint is prose or agent
instructions cannot change build output, so a Maven run proves nothing and only burns minutes.
Skip when **every** changed file is one of:
Expand Down Expand Up @@ -119,6 +130,7 @@ one is a red `main`.
- Minimum 80% coverage
- CUI Test Generator for test data (`@GeneratorsSource` preferred)
- **Forbidden**: Mockito, PowerMock, Hamcrest
- **A configuration key that parses is not a configuration key that acts.** Ask: *if the key were deleted entirely, would any test go red?* If not, the control it names is not test-proven — that is all a green suite settles, so trace the key to its production reader before concluding anything about whether it is in effect — see `doc/development/declared-limit-assertion-coverage.adoc`

### Javadoc

Expand Down
17 changes: 12 additions & 5 deletions demo-client/doc/playwright-suite.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,18 @@ regardless. The suite was therefore structurally incapable of observing this def
green throughout. It was not a weak test; it was a test of something else.

Generalise it before adding the next control: *any control whose enforcement lives in the browser --
`SameSite`, `Secure`, the `__Host-` prefix, CORS, `Referrer-Policy`, CSP -- cannot be proven by a
programmatic HTTP client, however faithful.* Only a real browser applies the policy. When you add or
change one, add the assertion to this Playwright suite; a green `Bff*IT` run tells you nothing about
it. That limitation is now recorded on `BffKeycloakLoginFlow` itself, so the next reader meets it at
the helper rather than in this document.
`SameSite`, `Secure`, the `__Host-` prefix, cookie partitioning, CORS, `Referrer-Policy`, CSP, and
the navigation context a response arrives in (a top-level redirect GET versus a cross-site form
POST) -- cannot be proven by a programmatic HTTP client, however faithful.* Only a real browser
applies the policy. The last member is the one the defect above turned on, and it is listed here so
the class matches the story rather than trailing it. *This Playwright suite is the only suite in
this repository that covers that class -- and it gates nothing.* It runs on pushes to `main` and on
demand, never on a pull request, so a pull request that breaks one of those controls can merge with
this suite never having run. Coverage and enforcement are separate things here, and only the first
one exists. When you add or change one of those controls, add the assertion here and run the suite
deliberately; a green `Bff*IT` run tells you nothing about it. That RestAssured limitation is now
recorded on `BffKeycloakLoginFlow` itself, so the next reader meets it at the helper rather than in
this document.
====

[#_module_layout]
Expand Down
11 changes: 10 additions & 1 deletion doc/development/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ to *run* the finished gateway).
merging over a red or stale-green gate, PR-new-code vs post-merge project-gate auditability,
and the fix-by-default / suppress-with-rationale escape hatch.

| link:build-gate-discipline.adoc[Build-Gate Discipline -- A Green Gate That Changed Your Files]
| The three mechanisms that mutate the working tree while the gate exits 0 -- OpenRewrite's
import-group churn, the pre-commit formatter's non-idempotency, and the `SimplifyTestThrows`
reversion -- the three consequences a contributor acts on (verify by surviving the gate,
attribute a dirty tree before reverting it, never hand-edit formatter-owned formatting), the
revert rule with the objection to it and the standing fixed-point debt, the recipe-scoping trap,
the non-gating `rewrite-report` job that reports all of it, the incremental-build rule, and the
recorded discard of the fully-qualified-refspec fix.

| link:release-process.adoc[Release Process]
| How a release is cut -- merge a version change in `.github/project.yml`, which is the publishing
act once the centrally-owned version-changed guard lets it through, with `workflow_dispatch` as the
Expand Down Expand Up @@ -71,7 +80,7 @@ to *run* the finished gateway).
`docker-compose.yml` and the mounted `gateway.yaml`, which stay the authoritative sources.

| link:declared-limit-assertion-coverage.adoc[Declared-Limit Assertion Coverage]
| Which declared gateway limit has a boundary assertion and which does not -- the twelve-file
| Which declared gateway limit has a boundary assertion and which does not -- the thirteen-file
descriptor surface, the strict preset's resolved caps that appear in no yaml, one row per limit
naming its enforcing component and its asserting test, the standing gap remainder, and the two
report-only findings -- that the body-cap wiring guard's glob never reaches the `endpoints/` tree,
Expand Down
Loading