Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/147-fileserver-payload-testkit-probes.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

184 lines
18 KiB
Plaintext

Sub-scope 06 (fileserver) payload operations · CSV export · testkit contracts · crash matrix
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
generatedAt=2026-08-30T01:30:13+00:00
=== OWNED FILES (3 main + 12 test) ===
d0566494c6024e6c068e980b7537200a0cf3844d main/LocalPersistentPayloadOperations.java 1101
140dca95e213bdc36c6a45a08a254904f9138c1d main/StreamingCsvEncoder.java 190
cb53573f65ec919834088b40343ad71352a8c754 main/FilesystemCsvExportAdapter.java 188
699b6db92aa73404f7319c5de7d1244354887286 test/LocalPersistentPayloadOperationsTest.java 757
2721049858a6857b49d089b87cd01677720d00ab test/FilesystemCsvExportAdapterTest.java 134
b8e0e713d8d87e39462ba229514e92c1c8195e6c test/FileserverCrashScenarioMain.java 473
819746893a4bc76be6b447a0ac9e19ad678ac24c test/testkit/ContentStoreContract.java 193
a0b7bac9cf0d04addfbe8b1700b2245509be6d8e test/testkit/CrashPoint.java 30
f7b14c62a7b19957859b23409991e8429916ca78 test/testkit/CrashRecoveryMatrixTest.java 173
c990e690ba089148cfcdc0dadeda05a6ca8617f9 test/testkit/LargeFileBoundedMemoryTest.java 152
1ad295c83d3cca34822b612e6cebb3857314f629 test/testkit/LocalContentStoreContractTest.java 42
2b44236a451ecc5f50f43badb8294787b58a95e6 test/testkit/MetadataPointerContentStoreContractTest.java 43
31f303c64278168dadc3fca71a51f38a51847cdb test/testkit/NfsAmbiguityIntegrationTest.java 42
3f1f7baf85d365968563052b6f1818b480ed678e test/testkit/NfsTestEnvironment.java 26
fdf5d1b0a0584dcc6eca86ada6be3298de15d462 test/testkit/PvcCertificationDescriptor.java 42
=== 8.1 reachability ===
$ git grep -rln 'LocalPersistentPayloadOperations\|StreamingCsvEncoder\|FilesystemCsvExportAdapter' -- . | grep -v "^adapter/outbound/fileserver/"
exit=1
$ grep -n 'class FilesystemCsvExportAdapter\|final class StreamingCsvEncoder\|final class LocalPersistentPayloadOperations' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java:25:public class FilesystemCsvExportAdapter implements FileExportPort {
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java:22:final class StreamingCsvEncoder {
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java:42:final class LocalPersistentPayloadOperations {
exit=0
=== 8.2 payload operations: the residual pathname primitives, declared ===
$ grep -n 'the JDK.s missing relative' -B 3 -A 3 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
35- * Secure payload boundary for the qualified local-persistent Fileserver R2 provider.
36- *
37- * <p>Caller values are converted to bounded generated segments before filesystem access. Reads,
38: * writes, and exact deletes use {@link SecureDirectoryStream}; the JDK's missing relative
39- * hard-link, directory-create, and directory-force primitives are bracketed by attested identity
40- * checks in this class.
41- */
exit=0
$ grep -n 'Files.createLink\|Files.createDirectory\|Files.newDirectoryStream\|Files.getFileStore\|Files.readAttributes\|Files.setPosixFilePermissions' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
414: Files.readAttributes(directory, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
789: Files.readAttributes(shardPath, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
802: Files.createDirectory(
805: Files.setPosixFilePermissions(shardPath, PRIVATE_DIRECTORY_PERMISSIONS);
807: Files.readAttributes(shardPath, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
813: return Files.getFileStore(path).name();
818: return Files.getFileStore(path).type();
934: Files.getFileStore(artifactPath).name(),
935: Files.getFileStore(artifactPath).type());
941: Files.createLink(link, existing);
1003: Files.getFileStore(path).name(),
1004: Files.getFileStore(path).type());
1013: DirectoryStream<Path> top = Files.newDirectoryStream(topDirectory);
exit=0
=== 8.2b CSV formula mitigation: which leading characters count ===
$ grep -n 'isFormulaCandidate' -A 10 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java
120: if (!isFormulaCandidate(value)) {
121- return value;
122- }
123- return switch (column.formulaPolicy()) {
124- case ALLOW -> value;
125- case MITIGATE -> {
126- formulaMitigated++;
127- yield "'" + value;
128- }
129- case REJECT ->
130- throw failure(
--
174: private static boolean isFormulaCandidate(String value) {
175- if (value.isEmpty()) {
176- return false;
177- }
178- return switch (value.charAt(0)) {
179- case '=', '+', '-', '@', '\t', '\r' -> true;
180- default -> false;
181- };
182- }
183-
184- private static FilePublicationException failure(
exit=0
$ grep -n 'formulaPolicy' -A 12 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java | head -18
123: return switch (column.formulaPolicy()) {
124- case ALLOW -> value;
125- case MITIGATE -> {
126- formulaMitigated++;
127- yield "'" + value;
128- }
129- case REJECT ->
130- throw failure(
131- FilePublicationException.Reason.INVALID_REQUEST,
132- "spreadsheet formula text rejected for column " + column.name());
133- };
134- }
135-
exit=0
=== 8.3 R1 legacy adapter vs R2 encoder: same job, declared difference ===
$ grep -n 'CREATE_NEW\|Files.write\|StringBuilder' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java | head
51: * to be assembled in one {@code StringBuilder} and then converted to a byte array, so peak memory
53: * And {@code Files.write} truncates, so exporting a name that already existed silently replaced
54: * whatever was there; {@code CREATE_NEW} makes a name collision an error the caller can see.
70: StandardOpenOption.CREATE_NEW,
exit=0
$ grep -n 'stand-in for NFS/SFTP' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java adapter/outbound/fileserver/README.md
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java:21: * (the "file server" boundary, a stand-in for NFS/SFTP). Fields are escaped per RFC-4180 and the
exit=0
=== 8.4 testkit: what the contracts and the crash matrix cover ===
$ grep -nE 'class |@Test| void |CrashPoint\.' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashPoint.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java | head -40
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:45:class CrashRecoveryMatrixTest {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:54: @EnumSource(CrashPoint.class)
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:55: void aPublishedObjectIsAlwaysCompleteAndDigestMatched(CrashPoint crashPoint) throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:63: assertThat(crashPoint).isNotEqualTo(CrashPoint.AFTER_METADATA_BEFORE_QUOTA);
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:75: @EnumSource(CrashPoint.class)
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:76: void aCrashNeverLeavesAPartialObjectUnderThePublishedKey(CrashPoint crashPoint) throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:100: if (crashPoint == CrashPoint.AFTER_CREATE) {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:105: if (crashPoint == CrashPoint.DURING_APPEND) {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:110: if (crashPoint == CrashPoint.AFTER_APPEND_COMMIT || crashPoint == CrashPoint.BEFORE_PUBLISH) {
exit=0
$ grep -nE 'class |@Test| void ' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java | head -30
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:39:public abstract class ContentStoreContract {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:50: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:51: void createAppendFinalizeStatReadDeleteRoundTrip() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:61: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:62: void appendsAccumulateAndTheDigestCoversEveryByte() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:73: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:74: void aMismatchedOffsetIsRefusedWithoutMutatingTheObject() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:86: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:87: void aRangedReadReturnsExactlyTheRequestedBytes() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:95: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:96: void aDeclaredDigestThatDoesNotMatchIsRefusedAtFinalize() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:113: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:114: void aDeclaredLengthThatDoesNotMatchIsRefusedAtFinalize() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:128: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:129: void deletingAnAbsentObjectIsAnIdempotentSuccessThatReportsTheDivergence() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:137: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:138: void theStoreReportsCapabilitiesItActuallyProved() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:143: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:144: void twoUploadsNeverShareAPhysicalKey() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java:18:class LocalContentStoreContractTest extends ContentStoreContract {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java:25: void createStore() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java:19:class MetadataPointerContentStoreContractTest extends ContentStoreContract {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java:26: void createStore() {
exit=0
$ grep -nE 'class |@Test| void |@EnabledIf|@Tag|Assumptions' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsTestEnvironment.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/PvcCertificationDescriptor.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java | head -30
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:19:@EnabledIfEnvironmentVariable(named = NfsTestEnvironment.ENABLE_FLAG, matches = "true")
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:20:class NfsAmbiguityIntegrationTest {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:25: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:26: void aRenameWhoseAcknowledgementWasLostIsAmbiguousRatherThanRetryable() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:31: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:32: void aStaleHandleAfterAServerRestartSendsTheFileToReconciliation() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:37: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:38: void theEnvironmentGateIsExplicitAboutWhyItWouldSkip() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsTestEnvironment.java:11:public final class NfsTestEnvironment {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:34:class LargeFileBoundedMemoryTest {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:43: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:44: void uploadingALargeObjectNeverRequestsMoreThanOneBufferAtATime() {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:71: @Test
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:72: void readingALargeObjectIsAlsoBounded() throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:116: private static final class CountingSource implements ReadableByteChannel {
exit=0
$ git grep -n 'FileserverCrashScenarioMain' -- 'adapter/outbound/fileserver/src' | head
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/FileserverCrashScenarioMain.java:50:final class FileserverCrashScenarioMain {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/FileserverCrashScenarioMain.java:65: private FileserverCrashScenarioMain() {}
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:31: private static final String CHILD_MAIN = FileserverCrashScenarioMain.class.getName();
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:36: @EnumSource(FileserverCrashScenarioMain.CrashBoundary.class)
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:38: FileserverCrashScenarioMain.CrashBoundary boundary) throws Exception {
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:43: assertThat(crashed.exitCode()).isEqualTo(FileserverCrashScenarioMain.CRASH_EXIT_CODE);
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:49: ? FileserverCrashScenarioMain.RECOVERY_INDETERMINATE_QUARANTINED
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:50: : FileserverCrashScenarioMain.RECOVERY_SUCCESS;
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:69: awaitMarker(holder, FileserverCrashScenarioMain.LOCK_ACQUIRED);
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:74: assertThat(excluded.output()).isEqualTo(FileserverCrashScenarioMain.LOCK_BUSY);
exit=0