Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/250-messaging-certification-gate-chain.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
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>
2026-09-04 22:51:59 +09:00

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