The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
111 lines
5.9 KiB
Plaintext
111 lines
5.9 KiB
Plaintext
# evidence 250 — messaging-certification-gate-chain
|
|
# revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
# cwd: /shared/codebase/clean-architecture-backend-template/src/messaging
|
|
# command: sed -n "36,130p" messaging-kafka/build.gradle; echo; echo ---CI JOB---; sed -n "44,52p" ../../.github/workflows/messaging-certification.yml
|
|
# ---- raw output ----
|
|
// `CertifiedEvidence` used to be a hand-written list of three scenarios, and every consumer of it —
|
|
// the compatibility matrix, the cross-broker release suite, the support matrix document — believed
|
|
// it without a container ever starting. The lane below produces that list, and
|
|
// `verifyMessagingCertificationEvidence` refuses to pass when the committed manifest claims a
|
|
// scenario this run did not prove. Editing the manifest by hand therefore fails the build; running
|
|
// the lane rewrites it.
|
|
strictTestLanes {
|
|
lane('messagingCertificationTest') {
|
|
tag = 'messaging-certification'
|
|
description = 'Runs the broker fault scenarios against a real Kafka and writes the ' +
|
|
'certification evidence the compatibility matrix reads.'
|
|
customize = { test ->
|
|
// Pinned images. A certification claim names the build it was made against, so a
|
|
// floating tag would make a red run unattributable and a green one unrepeatable.
|
|
// `-PmessagingKafkaImage=` overrides for a one-off run against another version.
|
|
test.systemProperty 'messaging.kafka.image',
|
|
(project.findProperty('messagingKafkaImage') ?: 'apache/kafka:4.1.0').toString()
|
|
test.systemProperty 'messaging.toxiproxy.image',
|
|
(project.findProperty('messagingToxiproxyImage')
|
|
?: 'ghcr.io/shopify/toxiproxy:2.12.0').toString()
|
|
test.systemProperty 'messaging.certification.manifest',
|
|
project.layout.buildDirectory
|
|
.file('messaging-certification/broker-certification-evidence.jsonl')
|
|
.get().asFile.absolutePath
|
|
// The commit is part of the evidence: "certified" is a claim about one source tree.
|
|
// Read from the environment rather than a `-P` flag so the CI job's command line stays
|
|
// the literal grammar the gate matrix lint accepts.
|
|
test.systemProperty 'messaging.certification.commit',
|
|
(project.findProperty('certificationCommit')
|
|
?: providers.environmentVariable('GITHUB_SHA').getOrElse('local'))
|
|
.toString()
|
|
}
|
|
}
|
|
}
|
|
|
|
// The certification tag is excluded from `test` because the lane deliberately carries no Docker
|
|
// guard. Every other container suite here skips with a stated reason when Docker is absent; a lane
|
|
// that skipped would report success for a broker nobody started, which is the whole failure the
|
|
// evidence exists to rule out. Keeping it out of `test` is what lets it fail closed without
|
|
// breaking a laptop build.
|
|
tasks.named('test', Test) {
|
|
useJUnitPlatform {
|
|
excludeTags 'quarantine', 'messaging-certification'
|
|
}
|
|
}
|
|
|
|
tasks.register('verifyMessagingCertificationEvidence') {
|
|
group = 'verification'
|
|
description = 'Fails when the committed broker certification manifest claims a scenario the ' +
|
|
'certification lane did not produce.'
|
|
dependsOn 'messagingCertificationTest'
|
|
|
|
def produced = layout.buildDirectory
|
|
.file('messaging-certification/broker-certification-evidence.jsonl')
|
|
def committed = rootProject.file(
|
|
'messaging/messaging-testkit/src/main/resources/messaging/' +
|
|
'broker-certification-evidence.jsonl')
|
|
inputs.file(produced)
|
|
inputs.file(committed)
|
|
outputs.file(layout.buildDirectory.file('reports/messaging-certification-evidence.txt'))
|
|
// A gate whose result can be served from an earlier run is evidence about that run.
|
|
outputs.upToDateWhen { false }
|
|
|
|
doLast {
|
|
// The commit and the observation instant differ on every run by design, so they are not
|
|
// part of the comparison — what has to match is which adapter proved which scenario against
|
|
// which image, and which test produced it.
|
|
Closure<Set<String>> claims = { File file ->
|
|
file.readLines('UTF-8')
|
|
.findAll { !it.trim().isEmpty() }
|
|
.collect { line ->
|
|
line.replaceAll(/,"gitCommit":"[^"]*"/, '')
|
|
.replaceAll(/,"observedAt":"[^"]*"/, '')
|
|
}
|
|
.toSet()
|
|
}
|
|
Set<String> ran = claims(produced.get().asFile)
|
|
Set<String> shipped = claims(committed)
|
|
|
|
if (ran != shipped) {
|
|
def unproven = shipped - ran
|
|
def unrecorded = ran - shipped
|
|
throw new GradleException(
|
|
"the committed certification manifest does not match this run.\n" +
|
|
" claimed but not produced: ${unproven.isEmpty() ? 'none' : unproven}\n" +
|
|
" produced but not claimed: ${unrecorded.isEmpty() ? 'none' : unrecorded}\n" +
|
|
"Copy ${produced.get().asFile} over ${committed} — the manifest is a " +
|
|
"record of a run, not a statement about one.")
|
|
}
|
|
def report = outputs.files.singleFile
|
|
report.parentFile.mkdirs()
|
|
report.text = "scenarios=${ran.size()} manifest=${committed}\n"
|
|
}
|
|
}
|
|
|
|
---CI JOB---
|
|
cache-dependency-path: |
|
|
src/**/*.gradle
|
|
src/**/gradle-wrapper.properties
|
|
src/**/gradle.lockfile
|
|
- name: Certify the Kafka adapter against a real broker
|
|
working-directory: src
|
|
# GITHUB_SHA is read by the lane and written into every evidence line, because "certified"
|
|
# is a claim about one source tree.
|
|
run: ./gradlew :messaging:messaging-kafka:verifyMessagingCertificationEvidence --no-daemon --stacktrace
|