From 6fdb79c93a94f14d1e507b632c35e0532970367d Mon Sep 17 00:00:00 2001 From: Conduction Release Bot Date: Sun, 9 Aug 2026 10:29:18 +0200 Subject: [PATCH] fix(gate-23): PDOK rule counted comments as violations; ADR-022 epoch never matched its comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two defects in lint-or-abstraction-anti-patterns.sh, found while measuring what would start hard-failing when the gate leaves WARN mode. 1. Rule 1 was `grep -rl api.pdok.nl`, so it matched prose. Measured across all 18 Conduction app repos at origin/development: three findings, two of them the opposite of a violation. procest src/services/pdokService.js — IS the openconnector-routed shim (BASE_URL = generateUrl('/apps/openconnector/api/pdok')). Its only match was a docblock line reading "Direct browser calls to api.pdok.nl are NOT permitted from this app — see Hydra umbrella `shared-pdok-via-openconnector` (ADR-022)". The gate reported a file as violating the rule because it contains a sentence citing the rule. openregister lib/Service/Geo/PdokGeocoder.php — matched a const holding the Locatieserver base URL, which is the argument handed to OpenConnector's CallService. The class owns no HTTP client and returns null when OpenConnector is absent. The prescribed pattern, reported as the forbidden one. Only procest lib/Service/Pdok/PdokLocatieserverService.php was real: its callDirect() fopen()s the endpoint whenever `pdok_locatieserver_source` is empty, which is the default. The rule now asks the two questions that separate them — is the host on a line of CODE, and does the file carry its own transport or dispatch through OpenConnector. Compliant and prose-only files are PRINTED as info lines, so a reader can tell "nothing there" from "found it and judged it compliant". It is narrower on prose only. A code-line URL with no demonstrable routing still fires, an unrecognised HTTP client still fires, and a comment naming openconnector buys nothing. scripts/lib/test_or_abstraction_pdok.sh pins all of that: 6 must-fire assertions against 4 must-be-silent ones, plus an empty-tree control so the silences mean something. Verified it can fail — widening the suppression to a whole-file grep turns it red. 2. BLOCK_AFTER_EPOCH never matched its own comment. The comment said "2026-05-11 + 90d", i.e. 2026-08-09 00:00 UTC = 1786233600. The committed value 1786636800 is 2026-08-13 16:00 UTC — four days and sixteen hours later, and not a midnight boundary, which is the tell it was arrived at by hand. The switch-over date could be read off neither. Intent was the comment's. Taking it literally would have flipped the gate to BLOCK the morning the discrepancy was found, so it was measured first: ELEVEN of 18 repos would start hard-failing, procest on an entire 26-class tenant stack and hermiq on a 6-class tenant control plane — an architecture programme, not a deadline. And on that same measurement most findings were artefacts of the rules' own matching. Moved deliberately to 2026-10-03, which is the epoch the ADR-051 capability table in this same file already uses, so ADR-022 has one fleet-wide enforcement date instead of two. The date moved on the record and says why. No rule was weakened to meet it and nothing was waived per file. --- .../scripts/lib/test_or_abstraction_pdok.sh | 200 ++++++++++++++++++ .../lint-or-abstraction-anti-patterns.sh | 149 ++++++++++++- 2 files changed, 343 insertions(+), 6 deletions(-) create mode 100755 hydra-gates/scripts/lib/test_or_abstraction_pdok.sh diff --git a/hydra-gates/scripts/lib/test_or_abstraction_pdok.sh b/hydra-gates/scripts/lib/test_or_abstraction_pdok.sh new file mode 100755 index 00000000..a95ecd5d --- /dev/null +++ b/hydra-gates/scripts/lib/test_or_abstraction_pdok.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: EUPL-1.2 +# +# test_or_abstraction_pdok.sh — self-test for rule 1 of +# scripts/lint-or-abstraction-anti-patterns.sh (shared-pdok-via-openconnector). +# +# WHY THIS EXISTS +# --------------- +# Until 2026-08-09 the rule was `grep -rl api.pdok.nl`. Measured across all 18 +# Conduction app repositories at origin/development it produced three findings, +# of which TWO were the opposite of a violation: +# +# * procest src/services/pdokService.js is the openconnector-routed shim; its +# only match was a docblock line saying direct calls are NOT permitted and +# citing this very rule by name. +# * openregister lib/Service/Geo/PdokGeocoder.php matched on a const holding +# the base URL that it hands to OpenConnector's CallService. It owns no +# HTTP client at all. +# +# The rule now reads code rather than prose, and distinguishes a file that +# dispatches through OpenConnector from one that carries its own transport. +# +# THE POINT OF THIS SUITE is the other direction. Narrowing a matcher is how a +# gate gets quietly neutered, so the FIRE assertions below are the ratchet: a +# real direct call must keep failing, and neither a comment naming +# "openconnector" nor an unrecognised HTTP client may buy silence. If a future +# edit widens the suppression, these go red. +# +# Run: bash scripts/lib/test_or_abstraction_pdok.sh (exit 0 = pass) +set -uo pipefail + +LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" +GATE="${LIB_DIR}/../lint-or-abstraction-anti-patterns.sh" + +if [ ! -f "${GATE}" ]; then + echo "FAIL — gate script not found at ${GATE}; this suite cannot assert anything." + echo "Refusing to report passes for a subject that is absent." + exit 1 +fi + +FAILS=0 +WORK="$(mktemp -d)" +trap 'rm -rf "${WORK}"' EXIT + +mkdir -p "${WORK}/lib/Service" "${WORK}/src" "${WORK}/appinfo" +printf '\n\n fixtureapp\n\n' > "${WORK}/appinfo/info.xml" + +# Force BLOCK mode so the exit status carries the verdict. In WARN mode the +# script returns 0 whether or not anything matched — which is exactly how these +# findings stayed invisible — so a suite that read the byte in WARN mode would +# assert nothing. +run_gate() ( + cd "${WORK}" && HYDRA_OR_GATE_BLOCK_AFTER_EPOCH=0 bash "${GATE}" >"${WORK}/.out" 2>&1 + echo $? +) + +assert_rc() { #