merge: integrate object storage production capability
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Object Storage Batch A Checkpoint
|
||||
|
||||
- Date: 2026-07-28
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Worktree:
|
||||
`/home/donghyeon/workspace/clean-architecture-backend-template-objectstorage`
|
||||
- Claimed level: R0 application contract only
|
||||
- Provider readiness advanced: no
|
||||
|
||||
## Implemented scope
|
||||
|
||||
- Characterized the legacy caller-key overwrite, whole-object materialization, locator exposure,
|
||||
eager filesystem directory creation, optional S3 bucket provisioning, and Poster transaction/API
|
||||
coupling without changing those behaviors.
|
||||
- Added provider-neutral identities, opaque checked references/handles, bounded streaming
|
||||
callbacks, content identity, digest/range values, requests, receipts, outcomes, and narrow ports
|
||||
under `dev.caskeleton.application.objectstorage`.
|
||||
- Required an `ObjectOperationKey` on mutation requests and separated normal publication,
|
||||
scan-maintenance, purge-maintenance, direct, and staged privilege surfaces.
|
||||
- Added recursive contract-purity tests and an ArchUnit freeze for the one existing sample legacy
|
||||
import.
|
||||
- Marked the legacy `ObjectStoragePort` and `StoredObject` as removal boundaries without adapting
|
||||
new semantic calls back to raw keys.
|
||||
|
||||
No provider-neutral kernel, canonical namespace/control codec, local R1 provider, S3/MinIO
|
||||
qualification, sample migration, or R2 readiness claim is included.
|
||||
|
||||
## TDD evidence
|
||||
|
||||
The planned RED checks failed only for the intentionally missing types or removal annotations:
|
||||
|
||||
- `ObjectStorageIdentityContractTest`: missing identity types before Task 2 implementation.
|
||||
- `ObjectContentContractTest` and `ObjectStorageValueContractTest`: missing content/value types
|
||||
before Task 3 implementation.
|
||||
- `ObjectStoragePortContractTest`: missing request/receipt/port family before Task 4 implementation.
|
||||
- `ObjectStorageArchitectureContractTest`: missing legacy removal annotations before Task 5
|
||||
implementation.
|
||||
|
||||
An initial ArchUnit DSL compilation error was a test-authoring error, not accepted as a RED result;
|
||||
the rule was corrected and rerun.
|
||||
|
||||
## GREEN verification
|
||||
|
||||
All commands ran from `src/` and completed with `BUILD SUCCESSFUL`:
|
||||
|
||||
```bash
|
||||
./gradlew :application-core:resolveAndLockAll --write-locks
|
||||
./gradlew :application-core:verifyDependencyLocks --console=plain
|
||||
./gradlew :application-core:test --tests '*ObjectStorageIdentityContractTest' --console=plain
|
||||
./gradlew :application-core:test \
|
||||
--tests '*ObjectContentContractTest' \
|
||||
--tests '*ObjectStorageValueContractTest' --console=plain
|
||||
./gradlew :application-core:test --tests '*ObjectStoragePortContractTest' --console=plain
|
||||
./gradlew :application-core:test \
|
||||
--tests '*ObjectStorageArchitectureContractTest' --console=plain
|
||||
./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest' --console=plain
|
||||
./gradlew :application-core:check --console=plain
|
||||
./gradlew verifyCleanArchitectureDependencies --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test :sample-portfolio:test --console=plain
|
||||
```
|
||||
|
||||
The final combined legacy focused suites completed in 27 seconds. Deprecation-for-removal warnings
|
||||
are expected evidence that legacy consumers remain visible; they are not suppressed.
|
||||
|
||||
## LLM Wiki capture
|
||||
|
||||
The canonical vault required by repository policy,
|
||||
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/`, and its parent
|
||||
`/home/donghyeon/workspace/ai-tool/` do not exist in this environment. Therefore the required
|
||||
`raw/branch-notes/codex-objectstorage-production-capability.md` could not be created or updated.
|
||||
No similarly named non-canonical clone was used. This exact access block is recorded in both the
|
||||
plan and design headers and here at the Batch A boundary.
|
||||
|
||||
## Remaining gates and risks
|
||||
|
||||
- External broker and REST consumers and deployed legacy data were not inspected; Gate A remains
|
||||
blocked for legacy removal or public API versioning.
|
||||
- The new contracts have no provider implementation yet.
|
||||
- The current legacy adapter retains whole-object and raw-locator behavior by design until the
|
||||
later migration batch.
|
||||
- No readiness registry row is promoted by this checkpoint.
|
||||
@@ -0,0 +1,109 @@
|
||||
# Object Storage Batch B Checkpoint
|
||||
|
||||
- Date: 2026-07-28
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Worktree:
|
||||
`/home/donghyeon/workspace/clean-architecture-backend-template-objectstorage`
|
||||
- Evidence grade: repository-local non-skipping unit/contract/application-context tests
|
||||
- Advanced cards: local managed single upload R1, local managed download R1
|
||||
- R2 or production-provider readiness advanced: no
|
||||
|
||||
## Implemented scope
|
||||
|
||||
- Added deterministic data/control namespaces, opaque reference/handle codecs, canonical request
|
||||
fingerprints, frozen binding/policy revisions, and bounded operation epochs.
|
||||
- Added six strict canonical JSON control-record families with fixed field order, outer SHA-256
|
||||
envelopes, schema/size checks, corruption rejection, and checked-in golden digests.
|
||||
- Added provider-neutral publication, scan, reference, direct-session, multipart, and pending-effect
|
||||
state transitions with same-operation replay and conflicting-intent rejection.
|
||||
- Added a provider contract and `filesystem-local-dev` implementation with bounded streaming,
|
||||
immutable exclusive create, SHA-256 verification, exact inspect/version, full/range transfer,
|
||||
conditional retirement, create resolution, restrictive permissions, and path/symlink
|
||||
confinement.
|
||||
- Added single-process exact-version control CAS and restart/corruption/fault characterization.
|
||||
Logical control keys use `.record` physical leaves locally so object-store-valid prefix/leaf key
|
||||
pairs cannot collide as filesystem file/directory paths.
|
||||
- Added constructor-bound `app.object-storage` settings and compile-before-construction
|
||||
provider/destination/route/policy binding. The capability is disabled by default and
|
||||
`filesystem-local-dev` is rejected for `prod`/`production`.
|
||||
- Added disabled, unselected, invalid, selected-success, selected-construction-failure, close,
|
||||
legacy-only, and namespace-separated dual-run composition tests.
|
||||
- Added semantic routing evidence for publish, replay without producer invocation, inspect,
|
||||
full transfer, absent reference, and exact retained route lookup.
|
||||
- Added the exact nine-card readiness registry. Only local managed single upload/download are R1;
|
||||
direct, multipart, quarantine, retention, and production reconciliation remain R0.
|
||||
|
||||
## TDD and defect evidence
|
||||
|
||||
Planned RED checks failed for the intentionally absent codec/kernel/provider/settings/readiness
|
||||
types before each implementation. Additional tests found and drove these corrections:
|
||||
|
||||
- Local control keys may legally have both a leaf and a child in object storage, while a filesystem
|
||||
cannot have both `reference` and `reference/lifecycle`; local physical `.record` mapping fixed the
|
||||
collision without changing logical keys.
|
||||
- `ObjectInspectionPort.inspect` initially threw for an absent known-route reference; it now
|
||||
returns `Optional.empty()` while incomplete/corrupt evidence still fails closed.
|
||||
- The application purity test initially scanned its own test output after a full `check`; it now
|
||||
derives the production class root from a production contract type.
|
||||
- The general B7 ArchUnit rule initially classified objectstorage provider-internal SPI/control
|
||||
return values as public adapter responses. The existing negative fixture remains active, while a
|
||||
dedicated non-empty rule now checks the actual objectstorage `*Adapter` semantic boundaries.
|
||||
|
||||
No skipped Docker or external-service test is used as Batch B readiness evidence.
|
||||
|
||||
## GREEN verification
|
||||
|
||||
All commands ran from `src/` unless noted and completed with `BUILD SUCCESSFUL` after the documented
|
||||
RED/fix cycles:
|
||||
|
||||
```bash
|
||||
./gradlew :adapter:outbound:objectstorage:verifyDependencyLocks --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*ObjectNamespaceCodecTest' \
|
||||
--tests '*ObjectRequestFingerprintCodecTest' \
|
||||
--tests '*ObjectOperationEpochTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*ObjectControlRecordCodecTest' \
|
||||
--tests '*ObjectOperationStateMachineTest' \
|
||||
--tests '*ObjectOperationKernelTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*ObjectStorageProviderContract' \
|
||||
--tests '*LocalDevObjectStorageProviderTest' \
|
||||
--tests '*LocalDevObjectStorageRecoveryTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*ObjectStorageBindingCompilerTest' \
|
||||
--tests '*ObjectStorageCapabilityConfigTest' \
|
||||
--tests '*RoutingObjectStorageAdapterTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*ObjectStorageReadinessRegistryTest' --console=plain
|
||||
./gradlew :sample-portfolio:test --console=plain
|
||||
./gradlew :application-core:check \
|
||||
:adapter:outbound:objectstorage:check --console=plain
|
||||
./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest' --console=plain
|
||||
./gradlew verifyCleanArchitectureDependencies --console=plain
|
||||
```
|
||||
|
||||
The final combined application/objectstorage checkpoint completed in 23 seconds. The focused
|
||||
Clean Architecture suite and dependency verification also passed.
|
||||
|
||||
## LLM Wiki capture
|
||||
|
||||
The canonical vault required by repository policy,
|
||||
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/`, and its parent
|
||||
`/home/donghyeon/workspace/ai-tool/` do not exist in this environment. Therefore the required
|
||||
`raw/branch-notes/codex-objectstorage-production-capability.md` and any derived raw documents could
|
||||
not be created or updated. No similarly named non-canonical clone was used. This exact access block
|
||||
is recorded in the plan/design status and at this Batch B boundary.
|
||||
|
||||
## Remaining gates and risks
|
||||
|
||||
- `filesystem-local-dev` has no multi-node linearizability or power-loss durability evidence and is
|
||||
forbidden in production profiles.
|
||||
- The canonical S3/MinIO provider contribution, async bounded transport, provider qualification,
|
||||
response-loss fault tests, and protected AWS evidence are not implemented.
|
||||
- Direct grants, multipart, quarantine/scan, retention/legal hold, privileged purge, reapers, and
|
||||
production reconciliation remain R0.
|
||||
- The sample Poster workflow still uses the deprecated whole-`byte[]` port and transaction-coupled
|
||||
legacy choreography. It is explicitly activated only in sample local/test configuration.
|
||||
- External API/broker consumers and deployed legacy data remain uninspected, so Gate A still blocks
|
||||
destructive migration or legacy removal.
|
||||
@@ -0,0 +1,84 @@
|
||||
# Object Storage Batch C Checkpoint
|
||||
|
||||
- Date: 2026-07-28
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Worktree:
|
||||
`/home/donghyeon/workspace/clean-architecture-backend-template-objectstorage`
|
||||
- Evidence grade: repository-local tests plus digest-pinned single-node MinIO/Toxiproxy tests
|
||||
- AWS execution: not authorized; source set compiled only
|
||||
- Production-provider readiness advanced: no
|
||||
|
||||
## Implemented scope
|
||||
|
||||
- Added exact AWS S3 and MinIO provider bindings, bounded evidence descriptors, qualifier/error
|
||||
mapping, secret references, endpoint/owner/addressing validation, and selected-only lifecycle
|
||||
construction.
|
||||
- Added bounded async request/response bridges and the managed S3 put, inspect, full/range download,
|
||||
checksum, exact-version, cancellation, and content-length paths.
|
||||
- Added canonical conditional S3 control storage and operation response-loss resolution. Provider
|
||||
ETags remain adapter-private and are never exposed as logical versions.
|
||||
- Added low-level managed multipart planning, sharded immutable part ledgers, initiate-before-I/O
|
||||
state, explicit create/upload/list/complete/abort calls, and exact completion verification.
|
||||
- Added non-skipping MinIO contract/fault lanes, an AWS compile-only qualification lane, a protected
|
||||
workflow, and gate-matrix coverage.
|
||||
|
||||
The exact MinIO image is
|
||||
`minio/minio@sha256:4c4a4876193f030c81f57aabb22bcb9a73462010eb61fcab66908e03e5484af8`.
|
||||
The exact Toxiproxy image is
|
||||
`ghcr.io/shopify/toxiproxy@sha256:9378ed52a28bc50edc1350f936f518f31fa95f0d15917d6eb40b8e376d1a214e`.
|
||||
|
||||
## Exact MinIO finding
|
||||
|
||||
Real-provider tests proved an asymmetric conditional profile:
|
||||
|
||||
- `PutObject If-None-Match: *` was accepted but overwrote an existing object.
|
||||
- stale `PutObject If-Match` was rejected with HTTP 412.
|
||||
- `CompleteMultipartUpload If-None-Match: *` was accepted and overwrote an existing object.
|
||||
- checksum, HEAD, and range behavior passed the exercised contract.
|
||||
|
||||
Because immutable create and create-if-absent control CAS cannot be proven, the exact MinIO managed
|
||||
and direct mutation profiles remain `UNSUPPORTED`. The implementation does not emulate missing
|
||||
atomicity with HEAD followed by an unconditional write and does not promote a readiness card.
|
||||
|
||||
## TDD and verification
|
||||
|
||||
The task-focused RED runs first failed on the planned absent binding, bridge, conditional store,
|
||||
multipart, and qualification types. Provider qualification then found the real MinIO conditional
|
||||
behavior above; the descriptor and negative contract were changed instead of weakening the
|
||||
contract.
|
||||
|
||||
Commands completed with `BUILD SUCCESSFUL`:
|
||||
|
||||
```bash
|
||||
cd src
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*S3ProviderBindingTest' \
|
||||
--tests '*S3ProviderQualifierTest' \
|
||||
--tests '*S3ProviderCompositionTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:objectStorageMinioContractTest --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:objectStorageMinioFaultTest --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:objectStorageAwsQualificationTestClasses --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:check --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:verifyDependencyLocks \
|
||||
verifyCleanArchitectureDependencies --console=plain
|
||||
bash ../.github/scripts/verify-gate-matrix.sh
|
||||
```
|
||||
|
||||
The gate matrix reports 22 gates: 21 verified and the protected AWS qualification gate explicitly
|
||||
`delegated-pending`.
|
||||
|
||||
## LLM Wiki capture
|
||||
|
||||
The canonical vault `/home/donghyeon/workspace/ai-tool/llm-wiki-private/` and its parent
|
||||
`/home/donghyeon/workspace/ai-tool/` do not exist in this environment. The required
|
||||
`raw/branch-notes/codex-objectstorage-production-capability.md` and derived raw documents could not
|
||||
be created or updated. No similarly named non-canonical clone was used.
|
||||
|
||||
## Remaining risks
|
||||
|
||||
- No AWS request was executed, so there is no observed AWS provider claim.
|
||||
- The pinned MinIO topology is a local single-node container and is not production TLS,
|
||||
multi-node, durability, or linearizability evidence.
|
||||
- The detailed managed multipart fault matrix is not exhaustive enough for R2.
|
||||
- No sample migration, public API, scan/publication choreography, retention, purge, or reaper is
|
||||
included in this checkpoint.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Object Storage Batch D Checkpoint
|
||||
|
||||
- Date: 2026-07-28
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Worktree:
|
||||
`/home/donghyeon/workspace/clean-architecture-backend-template-objectstorage`
|
||||
- Scope: direct-transfer provider/application primitives only
|
||||
- Public endpoint: none
|
||||
- Readiness advanced: no; all direct cards remain R0
|
||||
|
||||
## Implemented scope
|
||||
|
||||
- Added direct single-upload session policy, durable prepared/issued transitions, bearer
|
||||
redaction, exact completion verification, published-version download resolution, and an
|
||||
S3-presigner lifecycle owned by the selected provider.
|
||||
- Added direct multipart durable session and part-grant families, opaque acknowledgement tokens,
|
||||
sharded part records, admission-close/expiry fencing, exact ledger validation, completion/abort
|
||||
states, response-loss resolution, and persisted terminal exact-version replay.
|
||||
- Added direct S3 initiate/discovery, exact-part presign, `ListParts` acknowledgement, conditional
|
||||
complete followed by exact HEAD verification, and abort resolution.
|
||||
- Registered direct single and multipart delegates only when their exact compiled capability is
|
||||
selected. One presigner is constructed and closed exactly once.
|
||||
- Added golden canonical envelopes for the direct session, direct multipart session, and direct
|
||||
multipart grant families.
|
||||
- Fixed `MultipartCompleteRequest` null validation so valid immutable `List.of(...)` input no longer
|
||||
throws from `contains(null)`.
|
||||
|
||||
## Qualification truth
|
||||
|
||||
The exact MinIO release cannot prove create-only PUT or create-only multipart completion, so both
|
||||
direct profiles are explicitly `UNSUPPORTED`. The direct MinIO contract/fault lanes are negative
|
||||
admission tests: they prove no bearer or multipart mutation enters an unsupported profile. No test
|
||||
skip is used as positive evidence.
|
||||
|
||||
The AWS managed/direct source sets compile, but no AWS call was made and no AWS evidence row was
|
||||
published. No inbound controller, authorization surface, CORS runtime configuration, or public
|
||||
direct API exists.
|
||||
|
||||
## Verification
|
||||
|
||||
Commands completed with `BUILD SUCCESSFUL`:
|
||||
|
||||
```bash
|
||||
cd src
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*DirectTransferCoordinatorTest' \
|
||||
--tests '*PresignedGrantRedactionTest' \
|
||||
--tests '*S3DirectTransferProviderTest' \
|
||||
--tests '*ObjectControlRecordCodecTest' \
|
||||
--tests '*S3ProviderCompositionTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*DirectMultipartCoordinatorTest' \
|
||||
--tests '*DirectMultipartRaceTest' \
|
||||
--tests '*S3DirectMultipartProviderTest' \
|
||||
--tests '*ObjectControlRecordCodecTest' \
|
||||
--tests '*S3ProviderCompositionTest' --console=plain
|
||||
./gradlew \
|
||||
:adapter:outbound:objectstorage:objectStorageMinioContractTest \
|
||||
:adapter:outbound:objectstorage:objectStorageMinioFaultTest \
|
||||
--tests '*DirectTransfer*' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*DirectTransferCorsContractTest' --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:objectStorageAwsQualificationTestClasses --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:check --console=plain
|
||||
./gradlew :adapter:outbound:objectstorage:verifyDependencyLocks \
|
||||
verifyCleanArchitectureDependencies --console=plain
|
||||
./gradlew \
|
||||
:adapter:outbound:objectstorage:objectStorageMinioContractTest \
|
||||
:adapter:outbound:objectstorage:objectStorageMinioFaultTest --console=plain
|
||||
bash ../.github/scripts/verify-gate-matrix.sh
|
||||
./gradlew test --console=plain
|
||||
./gradlew check --console=plain
|
||||
```
|
||||
|
||||
The module `check` includes unit tests, Checkstyle, Spotless, SpotBugs, architecture, configuration
|
||||
processor, environment-key, and repository-wide policy checks. Existing test-only compiler
|
||||
warnings remain non-failing. The final repository-wide test run completed 79 tasks and the final
|
||||
repository-wide check completed 214 tasks.
|
||||
|
||||
## Deliberate limitations
|
||||
|
||||
- Issued bearer material is process-local. A restart fails closed instead of reconstructing or
|
||||
reissuing an already-issued bearer.
|
||||
- The signing clock/window is stored and bounded, but AWS SDK presigner query timing is not driven
|
||||
by the injected application clock.
|
||||
- The direct multipart recovery/race matrix covers its principal fences and completion response
|
||||
loss but is not exhaustive enough for an R2 claim.
|
||||
- Retention/Object Lock grant headers and a provider-enforced direct-single hard size ceiling are
|
||||
not qualified.
|
||||
- No public endpoint exists, so CORS evidence is a pure contract and no direct card may exceed R0
|
||||
in the current registry.
|
||||
|
||||
## Approval Gate A
|
||||
|
||||
Tasks 20–24 remain blocked until the user explicitly approves scanner ownership, the sample's
|
||||
first publication profile, the additive asynchronous API/status contract, and digest transport.
|
||||
No scan/publication/sample endpoint implementation was started.
|
||||
|
||||
## LLM Wiki capture
|
||||
|
||||
The canonical vault `/home/donghyeon/workspace/ai-tool/llm-wiki-private/` and its parent
|
||||
`/home/donghyeon/workspace/ai-tool/` do not exist in this environment. The required
|
||||
`raw/branch-notes/codex-objectstorage-production-capability.md` and derived raw documents could not
|
||||
be created or updated. No similarly named non-canonical clone was used.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Object Storage Batch E Pause Checkpoint
|
||||
|
||||
- Recorded: 2026-07-29 (Asia/Seoul)
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Worktree:
|
||||
`/home/donghyeon/workspace/clean-architecture-backend-template-objectstorage`
|
||||
- Status: implementation in progress; intentionally paused at the user's request
|
||||
- Evidence grade: local unit/integration/architecture evidence only; no AWS R2 evidence
|
||||
|
||||
## Implemented at this checkpoint
|
||||
|
||||
- Staged integrity verification, fake-scanner routing, publication handoff fencing, and stable
|
||||
replay receipts.
|
||||
- Additive Poster V8 dual-read schema (renumbered from branch-local V7 during JPA integration),
|
||||
upload/retirement intents, HMAC-sanitized idempotency scope,
|
||||
PostgreSQL atomic claim SPI, and forward-only migration qualification lane.
|
||||
- Short-transaction Poster image publication flow and additive locator-free `202` API under the
|
||||
AIP-122-compatible `/posters/{id}/imagePublications` collection.
|
||||
- Exact-reference/version logical retirement enqueue, lease/fence takeover, response-loss retry,
|
||||
Poster deletion survival, and disabled-by-default worker composition.
|
||||
- Isolated legacy migration contracts, report-only inspection, two-distinct-approver Ed25519
|
||||
approval verification, nonce replay boundary, and explicit maintenance-only composition.
|
||||
|
||||
## Verification completed
|
||||
|
||||
The following focused command passed after the final architecture fixes:
|
||||
|
||||
```bash
|
||||
cd src
|
||||
./gradlew \
|
||||
:sample-portfolio:spotlessApply \
|
||||
:sample-portfolio:test --tests '*PosterImagePublicationControllerWireTest' \
|
||||
:app-bootstrap:test --tests '*CleanArchitectureTest' \
|
||||
--console=plain
|
||||
```
|
||||
|
||||
The following focused suites also passed during this checkpoint:
|
||||
|
||||
```bash
|
||||
./gradlew :adapter:outbound:objectstorage:test \
|
||||
--tests '*LegacyObjectAdoptionServiceTest' \
|
||||
--tests '*LegacyAdoptionApprovalVerifierTest' \
|
||||
--tests '*ObjectStorageLegacyMigrationConfigTest' --console=plain
|
||||
|
||||
./gradlew :sample-portfolio:test \
|
||||
--tests '*DeletePosterImageRetirementTest' \
|
||||
--tests '*PosterImageRetirementCrashMatrixTest' \
|
||||
--tests '*PosterImageRetirementConfigTest' \
|
||||
--tests '*LegacyPosterImageUploadCharacterizationTest' --console=plain
|
||||
|
||||
./gradlew :sample-portfolio:test \
|
||||
--tests '*SampleApplicationContextTest' \
|
||||
:sample-portfolio:posterImageMigrationTest --console=plain
|
||||
```
|
||||
|
||||
The migration lane included
|
||||
`PosterImageRetirementQualificationTest`, which proved that an exact retirement row survives
|
||||
deletion of its Poster row.
|
||||
|
||||
## Failures found and resolved
|
||||
|
||||
- `spotlessJavaCheck` initially found formatting drift in newly changed application-core and
|
||||
persistence files. The owner-module Spotless apply tasks fixed it.
|
||||
- `SampleApplicationContextTest` initially failed because Spring's persistence exception advisor
|
||||
could not CGLIB-proxy the final `PosterImageAttachmentCasRepository`. Removing `final` fixed the
|
||||
context; the focused context suite then passed.
|
||||
- `CleanArchitectureTest` initially rejected an application-core return type from the sample domain
|
||||
and the kebab-case `image-publications` path. Conversion moved back to the application use case,
|
||||
and the endpoint changed to the repository's AIP-122-compatible `imagePublications` segment. The
|
||||
complete focused architecture suite then passed.
|
||||
|
||||
## Not yet re-run / not complete
|
||||
|
||||
- The combined Batch E checkpoint command stopped on the two architecture failures above before all
|
||||
requested root tasks could complete. The focused failing suites passed after the fixes, but
|
||||
`:sample-portfolio:check`, `verifyPublicPathSnapshot`, and the full combined Batch E command have
|
||||
not been re-run after those final fixes.
|
||||
- The complete repository `./gradlew test` and `./gradlew check` have not been re-run after the
|
||||
Batch E additions.
|
||||
- The legacy adoption runner/configuration is not yet wired to a production legacy inspector,
|
||||
permission-checked trust-key loader, or durable control-record replay-store implementation.
|
||||
- Tasks 25–30 (Batch F) have not started in this continuation.
|
||||
- Actual AWS qualification is blocked by Approval Gate B: no approved account, bucket/namespaces,
|
||||
workload roles, signed deployment attestation, or mutation/test authority was supplied.
|
||||
- No readiness card was promoted. Local/MinIO ceilings and unsupported conditional behavior remain
|
||||
unchanged.
|
||||
|
||||
## Wiki capture
|
||||
|
||||
At this isolated-branch checkpoint, the then-selected private vault
|
||||
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/` was absent, so capture was blocked. The final
|
||||
main integration was later captured in the user-designated public vault at
|
||||
`raw/branch-notes/chore-main-worktree-capability-integration.md`, with the derived error note
|
||||
`raw/errors/multi-worktree-contract-drift-2026-07-31.md`.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Object Storage Phase 0 Inventory
|
||||
|
||||
- Captured: 2026-07-28
|
||||
- Branch: `codex/objectstorage-production-capability`
|
||||
- Scope: repository-local source, tests, configuration, migrations, and documentation
|
||||
- Evidence grade: repository-local only; deployed data, broker subscribers, and external REST
|
||||
consumers were not inspected
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
rg -n 'application\.storage|ObjectStoragePort|StoredObject|ca-skeleton\.objectstorage|file://|s3://' \
|
||||
src docs
|
||||
rg -n 'image_key|posters/.*/image' src/sample-portfolio
|
||||
rg -n 'poster\.image-attached|StoredObjectResponse|PosterResponse|imageKey' \
|
||||
src/sample-portfolio docs
|
||||
```
|
||||
|
||||
The commands completed successfully in the isolated worktree. Results are classified below.
|
||||
Documentation hits in the Object Storage design/plan describe the migration and are not runtime
|
||||
consumers. The `s3://bucket/key-1` fixture in
|
||||
`IdempotencyStoreAdapterTest` belongs to the generic idempotency response-reference test and is not
|
||||
an Object Storage legacy-port consumer.
|
||||
|
||||
## Repository-local runtime inventory
|
||||
|
||||
| Contract/data | Producer | Repository-local consumers | Classification |
|
||||
| --- | --- | --- | --- |
|
||||
| `ObjectStoragePort` / `StoredObject` | `application-core/application/storage` | filesystem and S3 adapters, `UploadPosterImageUseCase`, `PosterController`/`PosterWebMapper` | legacy runtime contract |
|
||||
| `ca-skeleton.objectstorage.*` | `ObjectStorageSettings` / `ObjectStorageConfig` | sample runtime through its objectstorage runtime dependency | legacy runtime configuration |
|
||||
| `file://` receipt | `FilesystemObjectStorageAdapter` | `StoredObjectResponse.location` through `PosterWebMapper` | public legacy locator |
|
||||
| `s3://bucket/key` receipt | `S3ObjectStorageAdapter` | `StoredObjectResponse.location` through `PosterWebMapper` | public legacy locator |
|
||||
| `/posters/{id}/image` | `PosterController` | repository tests and the generated/public HTTP contract | legacy inbound API |
|
||||
| `StoredObjectResponse` | `PosterController` / `PosterWebMapper` | HTTP caller, with `key`, `size`, `contentType`, and `location` | legacy response DTO |
|
||||
| `PosterResponse.imageKey` | `PosterWebMapper` | list/get/create/update/publish/archive HTTP responses | legacy general response field |
|
||||
| `poster.image-attached` | `PosterEventPublisher` | no subscriber found in this repository | versionless broker event; external consumers unknown |
|
||||
| `poster.image-attached.imageKey` | `PosterImageAttached` and publisher JSON | no subscriber found in this repository | raw locator-shaped event field |
|
||||
| `poster.image_key` | Flyway V6, `PosterEntity`, persistence mapper | `Poster` aggregate and repository adapter | stored-data schema |
|
||||
| `posters/{id}/image` key | `UploadPosterImageUseCase` | aggregate `imageKey`, event payload, DB row, HTTP response | deterministic overwriteable legacy key |
|
||||
|
||||
## Executable characterization
|
||||
|
||||
The following tests pin the current behavior without approving it as the target design:
|
||||
|
||||
- `LegacyObjectStorageBehaviorTest`
|
||||
- caller-selected keys overwrite;
|
||||
- `get` returns `Optional<byte[]>` and materializes the whole object;
|
||||
- receipts expose `file://` and `s3://` locators.
|
||||
- `LegacyObjectStorageConfigTest`
|
||||
- missing backend configuration selects filesystem;
|
||||
- context creation creates the filesystem directory before the first write;
|
||||
- `autoCreateBucket=true` probes and creates a missing bucket during S3 bean construction.
|
||||
- `LegacyPosterImageUploadCharacterizationTest`
|
||||
- remote storage is called while `TransactionPort.inWrite` is active;
|
||||
- the controller calls `MultipartFile.getBytes`;
|
||||
- the response exposes raw key and provider location;
|
||||
- Poster deletion leaves the legacy object untouched.
|
||||
|
||||
## External inventory gap and Gate A
|
||||
|
||||
Repository search does not prove that the following have no deployed consumers:
|
||||
|
||||
- `POST /posters/{id}/image`;
|
||||
- `StoredObjectResponse.key` and `.location`;
|
||||
- `PosterResponse.imageKey`;
|
||||
- broker event type `poster.image-attached` and its `imageKey` payload;
|
||||
- rows already stored in `poster.image_key`;
|
||||
- filesystem/S3 objects already written under `posters/{id}/image`.
|
||||
|
||||
No deployed database, object namespace, access log, API client catalog, broker consumer group, schema
|
||||
registry, or owning team approval was inspected. Therefore removal, in-place field rename, event
|
||||
payload replacement, or legacy-object deletion remains blocked. Approval Gate A must obtain owner
|
||||
and consumer evidence and choose an additive/versioned migration contract.
|
||||
@@ -0,0 +1,114 @@
|
||||
schema_version: 1
|
||||
claims:
|
||||
- card_id: object-storage-managed-upload-single
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-managed-integrity
|
||||
claimed_level: R1
|
||||
evidence_revision: batch-b-local-r1
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":adapter:outbound:objectstorage:check"
|
||||
limitations:
|
||||
- single-process control CAS only; no multi-node linearizability
|
||||
- fsync and atomic move tests do not prove power-loss durability
|
||||
- local development provider is forbidden in production profiles
|
||||
|
||||
- card_id: object-storage-managed-upload-multipart
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- multipart publication protocol is not implemented
|
||||
|
||||
- card_id: object-storage-managed-download
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-managed-integrity
|
||||
claimed_level: R1
|
||||
evidence_revision: batch-b-local-r1
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":adapter:outbound:objectstorage:check"
|
||||
limitations:
|
||||
- local functional full and range reads are not production-provider qualification
|
||||
- no multi-node or power-loss durability claim
|
||||
|
||||
- card_id: object-storage-direct-upload-single
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- direct grant provider and public inbound endpoint are not implemented
|
||||
|
||||
- card_id: object-storage-direct-upload-multipart
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- direct multipart session and public inbound endpoint are not implemented
|
||||
|
||||
- card_id: object-storage-direct-download
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- direct download grant and public inbound endpoint are not implemented
|
||||
|
||||
- card_id: object-storage-quarantine-publication
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- scanner handoff and verdict fencing are not implemented
|
||||
|
||||
- card_id: object-storage-retention
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- retention and legal-hold provider enforcement are not implemented
|
||||
- privileged purge composition remains intentionally empty
|
||||
|
||||
- card_id: object-storage-reconciliation
|
||||
provider_type: filesystem-local-dev
|
||||
provider_version: jdk-21
|
||||
destination_profile: local-unimplemented
|
||||
claimed_level: R0
|
||||
evidence_revision: batch-b-contract-r0
|
||||
evidence_expires_on: ""
|
||||
required_tasks:
|
||||
- ":application-core:check"
|
||||
limitations:
|
||||
- local create resolution is single-process functional evidence only
|
||||
- production response-loss and multi-node reconciliation are not implemented
|
||||
@@ -1,10 +1,10 @@
|
||||
# Object Storage Production Capability Implementation Plan
|
||||
|
||||
- 작성일: 2026-07-28
|
||||
- 상태: 구현 계획 작성 완료, 모든 task 미착수, R2 미구현
|
||||
- 상태: Batch A–D 및 Batch E pause checkpoint를 main에 통합, Batch E 전체 gate와 R2는 미완료
|
||||
- 범위: 상세 설계 Phase 0–6; Phase 7/R3는 별도 승인 계획
|
||||
- LLM Wiki capture: 정본 vault
|
||||
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/` 부재로 차단; 비정본 clone 대체 사용 안 함
|
||||
- LLM Wiki capture: public vault의
|
||||
`raw/branch-notes/chore-main-worktree-capability-integration.md`에 main 통합 기록 완료
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILLS: use
|
||||
> `superpowers:subagent-driven-development` to execute independent tasks,
|
||||
@@ -43,6 +43,27 @@ provider-leaf split, a production malware-scanner implementation, and browser AP
|
||||
outside the approved contracts require follow-up plans. Writing this plan changes no runtime
|
||||
behavior and advances no readiness card.
|
||||
|
||||
**Implementation checkpoint (2026-07-28):** The Batch A–B application/kernel/local-provider
|
||||
baseline and the Batch C–D S3/MinIO managed/direct primitive slices are implemented and verified in
|
||||
`codex/objectstorage-production-capability`. Exact MinIO qualification proved that create-only PUT
|
||||
and multipart completion conditions are ignored, so its managed/direct mutation profiles remain
|
||||
`UNSUPPORTED`. AWS lanes compile only and have no observed evidence. Only the exact local managed
|
||||
single upload/download cards remain R1; every direct, multipart, quarantine, retention, and
|
||||
production reconciliation card remains R0. The exhaustive R2 fault/race matrices named by
|
||||
unchecked steps remain follow-up evidence, not implied completion. The legacy provider and Poster
|
||||
consumer remain isolated behind explicit sample-local/test activation. Evidence:
|
||||
[Batch A](../../evidence/object-storage/batch-a-checkpoint.md),
|
||||
[Batch B](../../evidence/object-storage/batch-b-checkpoint.md), and
|
||||
[Batch C](../../evidence/object-storage/batch-c-progress.md),
|
||||
[Batch D](../../evidence/object-storage/batch-d-checkpoint.md).
|
||||
|
||||
Batch E의 개별 focused fix 증거는
|
||||
[Batch E pause checkpoint](../../evidence/object-storage/batch-e-pause-checkpoint.md)에 기록됐다.
|
||||
당시 Batch E combined/root gate는 재실행되지 않았으므로 Batch E 전체 완료나 R2를 주장하지
|
||||
않는다. 격리 브랜치에서 잘못 선택했던 private vault 경로 대신, main 통합 작업은 사용자가
|
||||
지정한 public vault의 `raw/branch-notes/chore-main-worktree-capability-integration.md`에
|
||||
캡처했다.
|
||||
|
||||
---
|
||||
|
||||
## Execution rules
|
||||
@@ -154,7 +175,7 @@ Task 30; a batch is not closed if any output is missing.
|
||||
- Create:
|
||||
`docs/evidence/object-storage/phase-0-inventory.md`
|
||||
|
||||
- [ ] **Step 1: Record current executable behavior**
|
||||
- [x] **Step 1: Record current executable behavior**
|
||||
|
||||
Prove the current contract exactly as-is:
|
||||
|
||||
@@ -169,7 +190,7 @@ Prove the current contract exactly as-is:
|
||||
- the response exposes raw key/location;
|
||||
- Poster deletion does not retire the object.
|
||||
|
||||
- [ ] **Step 2: Verify the characterization baseline**
|
||||
- [x] **Step 2: Verify the characterization baseline**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -185,7 +206,7 @@ cd src
|
||||
|
||||
Expected: PASS against the current implementation. This is a baseline, not R1/R2 evidence.
|
||||
|
||||
- [ ] **Step 3: Inventory runtime and data dependencies**
|
||||
- [x] **Step 3: Inventory runtime and data dependencies**
|
||||
|
||||
In the evidence document record command output, not an unsupported repository-wide conclusion:
|
||||
|
||||
@@ -204,7 +225,7 @@ Classify every hit as legacy runtime, test, documentation, stored-data schema, o
|
||||
Record whether real deployed consumers/data were inspected; if they were not, state that external
|
||||
inventory is still required.
|
||||
|
||||
- [ ] **Step 4: Run unchanged focused suites**
|
||||
- [x] **Step 4: Run unchanged focused suites**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -232,7 +253,7 @@ Expected: PASS. No source behavior changes belong to this task.
|
||||
- Test:
|
||||
`src/application-core/src/test/java/dev/caskeleton/application/objectstorage/ObjectStorageIdentityContractTest.java`
|
||||
|
||||
- [ ] **Step 1: Add the approved test-only property engine**
|
||||
- [x] **Step 1: Add the approved test-only property engine**
|
||||
|
||||
Add `testImplementation 'net.jqwik:jqwik:1.9.1'`, matching the existing sample test version, and
|
||||
update only the application-core lockfile:
|
||||
@@ -245,7 +266,7 @@ cd src
|
||||
|
||||
Expected: PASS with test-only jqwik entries and no production dependency.
|
||||
|
||||
- [ ] **Step 2: Write the failing identity contract**
|
||||
- [x] **Step 2: Write the failing identity contract**
|
||||
|
||||
Test null/blank/control-character/oversize rejection, canonical round trips, operation-key
|
||||
composition, part range `1..10_000`, prefix separation, route-token grammar, reference check-digit
|
||||
@@ -253,7 +274,7 @@ tampering, and provider-locator non-disclosure. Include jqwik properties for arb
|
||||
input and a fixed golden vector for each prefix. Route existence/retirement is adapter binding
|
||||
state and is deliberately not tested or imported in `application-core`.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -265,7 +286,7 @@ cd src
|
||||
|
||||
Expected: compilation failure because the new identity types do not exist.
|
||||
|
||||
- [ ] **Step 4: Implement the minimum values**
|
||||
- [x] **Step 4: Implement the minimum values**
|
||||
|
||||
Use immutable final classes or records with constructor validation. `ObjectOperationKey` contains
|
||||
exactly destination, epoch, and operation ID. Public reference/handle types expose canonical text
|
||||
@@ -274,7 +295,7 @@ validation in framework-free Java (`MessageDigest`). The adapter-owned codec in
|
||||
minting path from a retained route token plus generated `ObjectId`; application values do not
|
||||
consult a route registry.
|
||||
|
||||
- [ ] **Step 5: Verify GREEN**
|
||||
- [x] **Step 5: Verify GREEN**
|
||||
|
||||
Run the command from Step 3 and
|
||||
`./gradlew :application-core:verifyDependencyLocks --console=plain`. Expected: PASS.
|
||||
@@ -299,7 +320,7 @@ Run the command from Step 3 and
|
||||
- Test:
|
||||
`src/application-core/src/test/java/dev/caskeleton/application/objectstorage/ObjectStorageValueContractTest.java`
|
||||
|
||||
- [ ] **Step 1: Write failing callback and value tests**
|
||||
- [x] **Step 1: Write failing callback and value tests**
|
||||
|
||||
Prove:
|
||||
|
||||
@@ -313,7 +334,7 @@ Prove:
|
||||
- range offset/length arithmetic rejects zero, negative, overflow, and over-budget delivery;
|
||||
- media types are canonical, bounded, and contain no control characters.
|
||||
|
||||
- [ ] **Step 2: Verify RED**
|
||||
- [x] **Step 2: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -326,14 +347,14 @@ cd src
|
||||
|
||||
Expected: compilation failure because the callback/value types do not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the minimum contract**
|
||||
- [x] **Step 3: Implement the minimum contract**
|
||||
|
||||
Reuse `CallBudget`; do not create serializable wall-clock deadlines. Callback types may declare only
|
||||
application exceptions and Java primitive/array types. Do not expose `InputStream`, `OutputStream`,
|
||||
`ByteBuffer`, Reactor, Flow, servlet, Spring, or AWS types. Document that callbacks are synchronous
|
||||
and blocking and that adapters must not invoke application callbacks on SDK event-loop threads.
|
||||
|
||||
- [ ] **Step 4: Verify GREEN**
|
||||
- [x] **Step 4: Verify GREEN**
|
||||
|
||||
Run the command from Step 2. Expected: PASS.
|
||||
|
||||
@@ -376,7 +397,7 @@ Run the command from Step 2. Expected: PASS.
|
||||
- Test:
|
||||
`src/application-core/src/test/java/dev/caskeleton/application/objectstorage/ObjectStoragePortContractTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing port-shape contract**
|
||||
- [x] **Step 1: Write the failing port-shape contract**
|
||||
|
||||
Use reflection and construction tests to prove:
|
||||
|
||||
@@ -393,7 +414,7 @@ Use reflection and construction tests to prove:
|
||||
- receipts expose no locator, ETag, upload ID, URL, credential, or provider enum;
|
||||
- requirements can strengthen but never lower destination policy.
|
||||
|
||||
- [ ] **Step 2: Verify RED**
|
||||
- [x] **Step 2: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -405,7 +426,7 @@ cd src
|
||||
|
||||
Expected: compilation failure because the semantic port family does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the minimum framework-free API**
|
||||
- [x] **Step 3: Implement the minimum framework-free API**
|
||||
|
||||
Follow design §9 exactly. Keep one public top-level type per file. Use immutable collections and
|
||||
defensive copies where required. Grants contain a bounded URI, signed header names/values,
|
||||
@@ -414,7 +435,7 @@ expiration, and opaque session identity, but their `toString` must redact the UR
|
||||
Document the separate scanner-workflow composition boundary on `ObjectScanMaintenancePort`; the
|
||||
normal staged port owns only stage, integrity verification, finalize, and abort.
|
||||
|
||||
- [ ] **Step 4: Verify GREEN**
|
||||
- [x] **Step 4: Verify GREEN**
|
||||
|
||||
Run the command from Step 2. Expected: PASS.
|
||||
|
||||
@@ -431,7 +452,7 @@ Run the command from Step 2. Expected: PASS.
|
||||
- Modify:
|
||||
`src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java`
|
||||
|
||||
- [ ] **Step 1: Write failing purity tests**
|
||||
- [x] **Step 1: Write failing purity tests**
|
||||
|
||||
Assert recursively through fields, methods, constructors, record components, generic arguments, and
|
||||
annotations that `dev.caskeleton.application.objectstorage..` has no Spring, AWS SDK, servlet,
|
||||
@@ -442,7 +463,7 @@ boundary. Add an ArchUnit rule that new sample business code may not import
|
||||
into an explicitly named legacy-only package, and permit the later exact
|
||||
`application.storage.migration` adoption use case as an admin-only compatibility exception.
|
||||
|
||||
- [ ] **Step 2: Verify RED**
|
||||
- [x] **Step 2: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -458,7 +479,7 @@ Expected: the new sample-import rule initially identifies the current legacy con
|
||||
fixture explicitly records it as the single frozen violation. No unrelated architecture violation
|
||||
may be accepted.
|
||||
|
||||
- [ ] **Step 3: Deprecate without adapting new calls back to raw keys**
|
||||
- [x] **Step 3: Deprecate without adapting new calls back to raw keys**
|
||||
|
||||
Mark both legacy types `@Deprecated(forRemoval = true)` and document:
|
||||
|
||||
@@ -473,13 +494,13 @@ Task 23 must move the remaining compatibility surface into an allowlisted `..pos
|
||||
slice; no non-legacy sample package may import the old port after that cutover. Do not create a
|
||||
semantic-to-legacy adapter that throws away operation identity or guarantees.
|
||||
|
||||
- [ ] **Step 4: Verify GREEN**
|
||||
- [x] **Step 4: Verify GREEN**
|
||||
|
||||
Run the command from Step 2. Expected: PASS with exactly the named frozen legacy violation and zero
|
||||
provider/framework leaks. The later migration exception must be bounded/redacted and visible only
|
||||
to the named administrative use case.
|
||||
|
||||
- [ ] **Step 5: Run Batch A checkpoint**
|
||||
- [x] **Step 5: Run Batch A checkpoint**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -523,7 +544,7 @@ object-storage provider.
|
||||
- Test:
|
||||
`src/adapter/outbound/objectstorage/src/test/java/dev/caskeleton/adapter/outbound/objectstorage/kernel/ObjectOperationEpochTest.java`
|
||||
|
||||
- [ ] **Step 1: Add the approved test-only property engine**
|
||||
- [x] **Step 1: Add the approved test-only property engine**
|
||||
|
||||
Add `testImplementation 'net.jqwik:jqwik:1.9.1'` and update only this leaf's lockfile:
|
||||
|
||||
@@ -535,7 +556,7 @@ cd src
|
||||
|
||||
Expected: PASS with no production jqwik dependency.
|
||||
|
||||
- [ ] **Step 2: Write failing codec and epoch tests**
|
||||
- [x] **Step 2: Write failing codec and epoch tests**
|
||||
|
||||
Use golden vectors and property tests for:
|
||||
|
||||
@@ -555,7 +576,7 @@ Use golden vectors and property tests for:
|
||||
- epoch `WARM -> ACTIVE -> DRAINING -> SEALED -> COMPACTED` transitions, no token reuse, and
|
||||
`OPERATION_EXPIRED` after seal/compaction.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -569,14 +590,14 @@ cd src
|
||||
|
||||
Expected: compilation failure because the codec/kernel types do not exist.
|
||||
|
||||
- [ ] **Step 4: Implement deterministic codecs**
|
||||
- [x] **Step 4: Implement deterministic codecs**
|
||||
|
||||
Keep all physical key construction in these codecs. Route lookup uses retained binding revision,
|
||||
never a current-provider default. The fingerprint includes exact content identity for R2 and an
|
||||
explicit `R1_UNVERIFIED_CONTENT` marker for compatibility; it never hashes content by materializing
|
||||
the object. Operation epoch records include finite replay/retention/compaction bounds.
|
||||
|
||||
- [ ] **Step 5: Verify GREEN**
|
||||
- [x] **Step 5: Verify GREEN**
|
||||
|
||||
Run the command from Step 3 and
|
||||
`./gradlew :adapter:outbound:objectstorage:verifyDependencyLocks --console=plain`. Expected: PASS.
|
||||
@@ -610,7 +631,7 @@ Run the command from Step 3 and
|
||||
- Test:
|
||||
`src/adapter/outbound/objectstorage/src/test/java/dev/caskeleton/adapter/outbound/objectstorage/kernel/ObjectOperationKernelTest.java`
|
||||
|
||||
- [ ] **Step 1: Write failing strict-codec tests**
|
||||
- [x] **Step 1: Write failing strict-codec tests**
|
||||
|
||||
For every record family introduced through Task 7 prove:
|
||||
|
||||
@@ -629,7 +650,7 @@ size/digest/media type, encryption/retention evidence, and immutable data versio
|
||||
small reference pointer CASes only the current manifest revision. No mutable pointer is treated as
|
||||
the manifest itself.
|
||||
|
||||
- [ ] **Step 2: Write failing transition-table tests**
|
||||
- [x] **Step 2: Write failing transition-table tests**
|
||||
|
||||
Cover every allowed and forbidden transition from design §12, including:
|
||||
|
||||
@@ -641,7 +662,7 @@ Cover every allowed and forbidden transition from design §12, including:
|
||||
- stale fence/revision rejection;
|
||||
- unknown/newer state never auto-deleted or downgraded.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -655,7 +676,7 @@ cd src
|
||||
|
||||
Expected: compilation failure because the control-plane and state-machine types do not exist.
|
||||
|
||||
- [ ] **Step 4: Implement the minimum kernel**
|
||||
- [x] **Step 4: Implement the minimum kernel**
|
||||
|
||||
The kernel accepts a compiled policy snapshot and an `ObjectControlStore`; it does not import a
|
||||
provider SDK. Reserve writes the frozen snapshot before provider mutation. Every mutation writes a
|
||||
@@ -665,7 +686,7 @@ discriminator; no reflective or `Map<String,Object>` binding and no undecided JS
|
||||
allowed. A later task that adds a durable family must modify this codec, add checked-in golden
|
||||
bytes, and prove old/new reader compatibility before the selected write version changes.
|
||||
|
||||
- [ ] **Step 5: Verify GREEN**
|
||||
- [x] **Step 5: Verify GREEN**
|
||||
|
||||
Run the command from Step 3. Expected: PASS.
|
||||
|
||||
@@ -689,7 +710,7 @@ Run the command from Step 3. Expected: PASS.
|
||||
- Test:
|
||||
`src/adapter/outbound/objectstorage/src/test/java/dev/caskeleton/adapter/outbound/objectstorage/filesystem/LocalDevObjectStorageRecoveryTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the reusable provider contract**
|
||||
- [x] **Step 1: Write the reusable provider contract**
|
||||
|
||||
The abstract suite must be executable for each provider and cover:
|
||||
|
||||
@@ -707,7 +728,7 @@ The abstract suite must be executable for each provider and cover:
|
||||
|
||||
Unsupported optional capabilities must assert descriptor `UNSUPPORTED`, not skip.
|
||||
|
||||
- [ ] **Step 2: Write failing local security/recovery tests**
|
||||
- [x] **Step 2: Write failing local security/recovery tests**
|
||||
|
||||
Use `@TempDir` and injected filesystem/fault collaborators to test:
|
||||
|
||||
@@ -720,7 +741,7 @@ Use `@TempDir` and injected filesystem/fault collaborators to test:
|
||||
- corrupt/newer records quarantined, never absent/deleted;
|
||||
- file descriptor and temporary-file cleanup.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -733,7 +754,7 @@ cd src
|
||||
|
||||
Expected: compilation failure because the local provider/kernel integration does not exist.
|
||||
|
||||
- [ ] **Step 4: Implement bounded local R1**
|
||||
- [x] **Step 4: Implement bounded local R1**
|
||||
|
||||
Use adapter-generated immutable data names, `CREATE_NEW`, bounded chunks, streaming SHA-256, staged
|
||||
temporary files, force/atomic move only where the host proves it, and strict relative path checks.
|
||||
@@ -741,7 +762,7 @@ The local control store serializes per operation in one process. When portabilit
|
||||
durability cannot be proven, return the truthful R1 descriptor; never claim multi-node CAS or
|
||||
power-loss durability. This provider is rejected in production profiles.
|
||||
|
||||
- [ ] **Step 5: Verify GREEN**
|
||||
- [x] **Step 5: Verify GREEN**
|
||||
|
||||
Run the command from Step 3. Expected: PASS.
|
||||
|
||||
@@ -770,7 +791,7 @@ Run the command from Step 3. Expected: PASS.
|
||||
- Modify:
|
||||
`src/sample-portfolio/src/main/resources/application.yml`
|
||||
|
||||
- [ ] **Step 1: Write failing binding tests**
|
||||
- [x] **Step 1: Write failing binding tests**
|
||||
|
||||
Bind `app.object-storage` and reject:
|
||||
|
||||
@@ -788,7 +809,7 @@ Bind `app.object-storage` and reject:
|
||||
|
||||
Prove a valid local profile compiles one exact route and immutable policy snapshot.
|
||||
|
||||
- [ ] **Step 2: Write failing composition tests**
|
||||
- [x] **Step 2: Write failing composition tests**
|
||||
|
||||
Use `ApplicationContextRunner` to prove:
|
||||
|
||||
@@ -805,7 +826,7 @@ Use `ApplicationContextRunner` to prove:
|
||||
- a namespace-separated canonical `legacy` subgroup plus the new capability may run together only
|
||||
in the explicit migration/sample-local profile.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -819,7 +840,7 @@ cd src
|
||||
Expected: compilation/test failure because canonical settings/composition do not exist and current
|
||||
legacy configuration activates filesystem by default.
|
||||
|
||||
- [ ] **Step 4: Implement exact binding and activation**
|
||||
- [x] **Step 4: Implement exact binding and activation**
|
||||
|
||||
Use immutable constructor-bound settings and typed `Duration`/`DataSize`/enums. Compile settings
|
||||
before constructing any provider. Contributions are side-effect-free descriptors; the assembler
|
||||
@@ -838,11 +859,11 @@ Until Task 22 migrates the consumer, the sample-local YAML explicitly enables th
|
||||
filesystem seam and may also select `filesystem-local-dev` at `./.data/object-storage-v1` for the
|
||||
new capability. No production YAML receives a local fallback.
|
||||
|
||||
- [ ] **Step 5: Verify GREEN**
|
||||
- [x] **Step 5: Verify GREEN**
|
||||
|
||||
Run the command from Step 3. Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Prove the stable contribution seam**
|
||||
- [x] **Step 6: Prove the stable contribution seam**
|
||||
|
||||
Add an `ApplicationContextRunner` matrix with a counting fake contribution: disabled, unselected,
|
||||
invalid binding, selected success, selected construction failure, and close. Prove construction
|
||||
@@ -874,7 +895,7 @@ unverified post-GREEN addition.
|
||||
- Modify:
|
||||
`docs/superpowers/specs/2026-07-28-objectstorage-production-capability-design.md`
|
||||
|
||||
- [ ] **Step 1: Write the failing registry/schema test**
|
||||
- [x] **Step 1: Write the failing registry/schema test**
|
||||
|
||||
Validate the frozen schema and exact nine card IDs. Reject:
|
||||
|
||||
@@ -886,7 +907,7 @@ Validate the frozen schema and exact nine card IDs. Reject:
|
||||
- local-dev above R1;
|
||||
- a limitation-free row when a provider descriptor reports limitations.
|
||||
|
||||
- [ ] **Step 2: Verify RED**
|
||||
- [x] **Step 2: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -898,13 +919,13 @@ cd src
|
||||
|
||||
Expected: failure because the registry and runtime card types do not exist.
|
||||
|
||||
- [ ] **Step 3: Add only evidenced claims**
|
||||
- [x] **Step 3: Add only evidenced claims**
|
||||
|
||||
Initial entries may claim R0 for contract-only cards and R1 for local managed single upload/download
|
||||
only after Tasks 6–9 pass. Direct, multipart, quarantine, retention, and production reconciliation
|
||||
remain R0/unimplemented. Document that local process recovery is not multi-node CAS or R2.
|
||||
|
||||
- [ ] **Step 4: Run Batch B checkpoint**
|
||||
- [x] **Step 4: Run Batch B checkpoint**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -919,7 +940,7 @@ cd src
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Verify rollback**
|
||||
- [x] **Step 5: Verify rollback**
|
||||
|
||||
Start a context with the canonical capability disabled and legacy disabled. Assert there are no
|
||||
storage beans or side effects. Start the explicit legacy profile and prove old data remains
|
||||
@@ -945,7 +966,7 @@ readable. No migration or deletion occurs at startup.
|
||||
- Test:
|
||||
`src/adapter/outbound/objectstorage/src/test/java/dev/caskeleton/adapter/outbound/objectstorage/s3/S3AsyncClientFactoryTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing pinned-API characterization**
|
||||
- [x] **Step 1: Write the failing pinned-API characterization**
|
||||
|
||||
Compile and assert the exact `2.30.0` API used by later tasks:
|
||||
|
||||
@@ -960,7 +981,7 @@ Add boundary tests at `Integer.MAX_VALUE`, `Integer.MAX_VALUE + 1L`, part count
|
||||
minimum S3 non-final part size. Record the full-object multipart checksum profile as unsupported
|
||||
above the SDK integer boundary unless an independently approved path exists.
|
||||
|
||||
- [ ] **Step 2: Write failing client-policy tests**
|
||||
- [x] **Step 2: Write failing client-policy tests**
|
||||
|
||||
Reject missing/non-positive/contradictory:
|
||||
|
||||
@@ -974,7 +995,7 @@ Reject missing/non-positive/contradictory:
|
||||
Assert the factory uses `S3AsyncClient` plus explicit `NettyNioAsyncHttpClient`, not CRT or the sync
|
||||
client, and owns close order.
|
||||
|
||||
- [ ] **Step 3: Verify RED**
|
||||
- [x] **Step 3: Verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -988,14 +1009,14 @@ cd src
|
||||
Expected: compilation failure because the client factory and compile-scoped Netty async client do
|
||||
not exist.
|
||||
|
||||
- [ ] **Step 4: Add only the required dependency and implementation**
|
||||
- [x] **Step 4: Add only the required dependency and implementation**
|
||||
|
||||
Add `software.amazon.awssdk:netty-nio-client` under the existing AWS BOM. Keep
|
||||
`software.amazon.awssdk:s3` and the BOM at `2.30.0`; do not add CRT or Transfer Manager. Configure
|
||||
finite client and HTTP timeouts/pools/retry from the compiled policy. Default-chain credentials are
|
||||
resolved only after the provider is selected.
|
||||
|
||||
- [ ] **Step 5: Regenerate and verify locks**
|
||||
- [x] **Step 5: Regenerate and verify locks**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -1752,6 +1773,13 @@ Approval Gate A.
|
||||
|
||||
## Approval Gate A — Scanner ownership and sample public contract
|
||||
|
||||
**Approved 2026-07-29:** The user authorized continued implementation using the recommended
|
||||
defaults. The deep design now records the exact decisions: test-fake scanner only,
|
||||
`integrity-verified-reference`, additive POST/GET with stable `202`, Base64 SHA-256 multipart
|
||||
field, required keyed idempotency with atomic TX1 reservation/intent, existing `poster:write`,
|
||||
legacy compatibility profile, new V2 event without v1 removal, intentionally scoped OpenAPI
|
||||
snapshot, and additive forward-only V8 dual read.
|
||||
|
||||
Do not execute Tasks 20–24 until the approved design records all of the following:
|
||||
|
||||
- staged scan fencing may be implemented with a test fake, but no production scanner/provider is
|
||||
@@ -1759,7 +1787,7 @@ Do not execute Tasks 20–24 until the approved design records all of the follow
|
||||
- the sample's first migrated profile is either `integrity-verified-reference` or an explicitly
|
||||
named scan-gated profile with a real scanner owner;
|
||||
- the additive endpoint proposal
|
||||
`POST /posters/{id}/image-publications`, status, authorization, request size/media policy, and
|
||||
`POST /posters/{id}/imagePublications`, status, authorization, request size/media policy, and
|
||||
locator-free response fields;
|
||||
- the asynchronous response contract: POST always returns the same bounded `202` reservation
|
||||
(opaque publication operation plus status link) replayed by `IdempotencyExecutor`; a separately
|
||||
@@ -1783,8 +1811,8 @@ Do not execute Tasks 20–24 until the approved design records all of the follow
|
||||
window, consumer migration evidence, rollback, and zero-consumer proof before v1 removal. Never
|
||||
rename `imageKey` to `reference` under the same unversioned event contract;
|
||||
- intentional OpenAPI snapshot approval;
|
||||
- the additive V7 schema and forward-only rollback window.
|
||||
- the compatibility model during V7: existing legacy attachments remain readable/publishable,
|
||||
- the additive V8 schema and forward-only rollback window.
|
||||
- the compatibility model during V8: existing legacy attachments remain readable/publishable,
|
||||
legacy writes are restricted to the compatibility controller/profile, and the canonical profile
|
||||
writes only READY opaque references.
|
||||
|
||||
@@ -1902,7 +1930,7 @@ scanner, `object-storage-quarantine-publication` remains below R2.
|
||||
- Modify:
|
||||
`src/sample-portfolio/gradle.lockfile`
|
||||
- Create:
|
||||
`src/sample-portfolio/src/main/resources/db/sample-migration/V7__poster_image_publication.sql`
|
||||
`src/sample-portfolio/src/main/resources/db/sample-migration/V8__poster_image_publication.sql`
|
||||
- Create under
|
||||
`src/sample-portfolio/src/main/java/dev/caskeleton/sample/portfolio/application/posterimage/`:
|
||||
`PosterImageUploadIntent.java`, `PosterImageUploadIntentState.java`,
|
||||
@@ -1954,7 +1982,7 @@ scanner, `object-storage-quarantine-publication` remains below R2.
|
||||
- Test:
|
||||
`src/sample-portfolio/src/test/java/dev/caskeleton/sample/portfolio/bootstrap/objectstorage/PosterImageIdempotencyConfigTest.java`
|
||||
- Create:
|
||||
`src/sample-portfolio/src/posterImageMigrationTest/java/dev/caskeleton/sample/portfolio/qualification/PosterImageV7MigrationQualificationTest.java`
|
||||
`src/sample-portfolio/src/posterImageMigrationTest/java/dev/caskeleton/sample/portfolio/qualification/PosterImageV8MigrationQualificationTest.java`
|
||||
- Create:
|
||||
`src/sample-portfolio/src/posterImageMigrationTest/java/dev/caskeleton/sample/portfolio/qualification/PosterImageIdempotencyRotationQualificationTest.java`
|
||||
- Modify:
|
||||
@@ -1986,7 +2014,7 @@ Expected: the task/configuration exists and locks are valid; no migration behavi
|
||||
|
||||
Run real PostgreSQL/Flyway and prove:
|
||||
|
||||
- fresh V1–V7 and V6→V7 upgrade;
|
||||
- fresh V1–V8 and V7→V8 upgrade;
|
||||
- V6 is unchanged;
|
||||
- existing `poster.image_key` rows remain readable through the internal dual-read mapper;
|
||||
- new READY attachments store only bounded opaque `image_reference`;
|
||||
@@ -2004,7 +2032,7 @@ Run real PostgreSQL/Flyway and prove:
|
||||
- replacement/delete work uses a separate durable `poster_image_retirement_intent` row and cannot
|
||||
be inferred from a deleted Poster or overloaded upload intent.
|
||||
|
||||
The V7 intent table must include stable operation identity/epoch/destination, request fingerprint,
|
||||
The V8 intent table must include stable operation identity/epoch/destination, request fingerprint,
|
||||
bounded idempotency-scope digest, expected poster version, exact content identity/media type, state,
|
||||
stage handle, handoff claim/fence/expiry, published reference/version, replaced reference,
|
||||
retry/error timestamps, and optimistic revision. Index the unique scope, operation, worker claims,
|
||||
@@ -2047,7 +2075,7 @@ cd src
|
||||
./gradlew :sample-portfolio:posterImageMigrationTest --console=plain
|
||||
```
|
||||
|
||||
Expected: compilation/migration failures because V7, the intent model, key-ring binding, and
|
||||
Expected: compilation/migration failures because V8, the intent model, key-ring binding, and
|
||||
non-skipping qualification lane do not exist. Docker/PostgreSQL absence is an actionable failure,
|
||||
not a skip or passing default test.
|
||||
|
||||
@@ -2084,7 +2112,7 @@ Expected: PASS, and the `posterImageMigrationTest` result XML reports zero skipp
|
||||
|
||||
- [ ] **Step 6: Verify rollback window**
|
||||
|
||||
The non-skipping `PosterImageV7MigrationQualificationTest` must run a V6-compatible SQL/JPA
|
||||
The non-skipping `PosterImageV8MigrationQualificationTest` must run a V7-compatible SQL/JPA
|
||||
projection against the expanded schema, including old INSERT/UPDATE/read behavior. Document that
|
||||
rollback is binary-only while old code ignores additive columns; there is no down migration,
|
||||
column drop, intent deletion, or object mutation.
|
||||
@@ -2313,7 +2341,7 @@ idempotency package. Preserve the existing application port contract and prove w
|
||||
expired-find→reclaim→complete with new fingerprint/expiry/COMPLETED state, and unrelated managed
|
||||
entity preservation in non-skipping real PostgreSQL. Do not emulate with a process lock. Update the
|
||||
owner README/CLAUDE vendor SPI table. The same qualification queries both `idempotency_record` and
|
||||
V7 intent tables and proves neither contains the raw Idempotency-Key, tenant, or principal; stored
|
||||
V8 intent tables and proves neither contains the raw Idempotency-Key, tenant, or principal; stored
|
||||
aliases/digests retain tenant separation.
|
||||
|
||||
Reconciliation settings are constructor-bound, disabled by default, and bound batch size,
|
||||
@@ -2671,7 +2699,7 @@ migration-port/verifier/replay-store/runner beans and zero side effects. The clo
|
||||
registers approval replay records with golden/new-reader/old-reader compatibility fixtures.
|
||||
|
||||
Retirement settings are constructor-bound and disabled by default, with bounded claim/renew,
|
||||
batch/page, delay, retry/backoff, concurrency, and shutdown grace. The job claims the dedicated V7
|
||||
batch/page, delay, retry/backoff, concurrency, and shutdown grace. The job claims the dedicated V8
|
||||
retirement table and calls only logical exact-reference retirement outside DB transactions. A
|
||||
disabled context creates no scheduler/thread; stale fence/takeover is rejected and response loss
|
||||
remains operation-keyed exact-reference/version reconciliation, not blind retry. TX3 replacement and
|
||||
@@ -3419,7 +3447,7 @@ gates.
|
||||
Before the implementation completion response, read the canonical vault instructions and update:
|
||||
|
||||
```text
|
||||
/home/donghyeon/workspace/ai-tool/llm-wiki-private/raw/branch-notes/<branch-name>.md
|
||||
/home/donghyeon/workspace/ai-tools/llm-wiki/raw/branch-notes/<branch-name>.md
|
||||
```
|
||||
|
||||
Record files, decisions, test commands/results, failures/blocks, evidence grade, and derivative
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Object Storage Production Capability Deep Design
|
||||
|
||||
- 작성일: 2026-07-28
|
||||
- 상태: 상세 설계 및 구현 계획 완료, 구현 미착수, R2 미구현
|
||||
- 상태: Approval Gate A 승인 반영, Batch A–D 및 Batch E pause checkpoint를 main에 통합, R2 미구현
|
||||
- 독립 재리뷰: 완료 — 아키텍처 및 문서 실행성 blocker 0, high 0
|
||||
- LLM Wiki capture: 정본 vault
|
||||
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/` 부재로 차단; 비정본 clone 대체 사용 안 함
|
||||
- LLM Wiki capture: public vault의
|
||||
`raw/branch-notes/chore-main-worktree-capability-integration.md`에 main 통합 기록 완료
|
||||
- 기준: Java 21, Spring Boot 4.0.0, Gradle 멀티모듈 Clean Architecture
|
||||
- 대상 leaf: `adapter-outbound-objectstorage`
|
||||
- 현재 구현 수준: whole-object `byte[]` 기반 local filesystem/S3-MinIO 예제, R0~R1 일부
|
||||
- 현재 구현 수준: provider-neutral kernel + `filesystem-local-dev` managed single R1,
|
||||
S3 managed/direct primitives, exact MinIO mutation profiles `UNSUPPORTED`,
|
||||
legacy whole-object filesystem/S3-MinIO 격리
|
||||
- 상위 문서:
|
||||
[Production Capability Platform Design](2026-07-26-production-capability-platform-design.md)
|
||||
- 구현 계획:
|
||||
@@ -19,39 +21,100 @@
|
||||
|
||||
## 0. 구현 상태
|
||||
|
||||
2026-07-28 기준 구현된 범위는 다음뿐이다.
|
||||
### Approval Gate A 결정 — 2026-07-29
|
||||
|
||||
사용자의 구현 계속 승인에 따라 Batch E의 public/sample 계약을 다음과 같이 고정한다.
|
||||
|
||||
- production scanner는 주장하지 않는다. 스캔 경계는 test fake만 제공하고 첫 sample
|
||||
destination profile은 `integrity-verified-reference`로 한다.
|
||||
- canonical surface는 additive
|
||||
`POST /posters/{id}/imagePublications`와 별도 authorized GET status resource다. 저장소의
|
||||
AIP-122 경로 규칙에 따라 collection segment에는 kebab-case를 사용하지 않는다.
|
||||
POST는 항상 동일한 bounded `202` reservation을 반환하며 READY 결과를 같은 idempotency
|
||||
response에 섞지 않는다.
|
||||
- 정확한 full-file SHA-256은 canonical Base64 형식의 bounded multipart field로 받고 TX1 전에
|
||||
semantic fingerprint에 포함한 뒤 staging 중 다시 검증한다.
|
||||
- `Idempotency-Key`는 필수다. 첫 committed UploadIntent가 stable operation ID를 할당하며
|
||||
generic idempotency reservation과 intent는 같은 TX1에서 원자적으로 commit한다.
|
||||
- sample authorization은 기존 `poster:write` permission을 유지한다. 존재하지 않는 Poster
|
||||
owner 속성을 발명하지 않는다.
|
||||
- legacy `/posters/{id}/image`는 명시적 compatibility profile에서만 유지한다. canonical과
|
||||
legacy controller는 동시에 활성화하지 않는다.
|
||||
- 기존 `poster.image-attached`/`imageKey` 이벤트는 변경하거나 제거하지 않는다. canonical
|
||||
흐름은 새 versioned V2 event를 사용하며 외부 zero-consumer를 주장하지 않는다.
|
||||
- OpenAPI 승인은 새 publication/status surface에만 한정한다. 기존 DTO의 locator-shaped
|
||||
필드는 compatibility window 동안 그대로 둔다.
|
||||
- 통합 타임라인의 V8은 additive, forward-only다. V7 Poster schema의 `image_key`와 legacy
|
||||
reader를 유지하고 canonical writer는
|
||||
READY opaque reference만 기록한다. rollback은 구 binary가 additive columns를 무시하는
|
||||
binary-only window이며 down migration이나 자동 object mutation은 없다.
|
||||
|
||||
2026-07-28 Batch D 체크포인트 기준 다음 범위가 구현되었다.
|
||||
|
||||
- `application-core`의 framework/provider-neutral object-storage identity, opaque reference,
|
||||
bounded content callback, digest/range, request/receipt/outcome, capability requirement;
|
||||
- managed, staged, direct single/multipart, scan-maintenance, purge-maintenance를 분리한 narrow port;
|
||||
- 모든 mutation의 `ObjectOperationKey`, strict reference grammar/checksum, transient grant redaction;
|
||||
- application contract의 framework/provider/transport/persistent-locator purity 검사;
|
||||
- legacy port/receipt의 removal-boundary deprecation과 sample legacy import freeze;
|
||||
- deterministic `data/v1`/`control/v1` namespace, reference/handle/fingerprint/policy codec,
|
||||
operation epoch와 immutable route/policy snapshot;
|
||||
- strict canonical JSON control envelope와 operation/manifest/reference/multipart record,
|
||||
reserve/replay/conflict/pending-effect state kernel;
|
||||
- bounded immutable local create, exact inspect/version, full/range transfer, conditional retire,
|
||||
create resolution, symlink/path confinement, restrictive permissions와 single-process CAS;
|
||||
- constructor-bound `app.object-storage` settings, compile-before-construction provider
|
||||
contribution/assembler, destination/retained-route routers, disabled/unselected/invalid/close
|
||||
side-effect matrix;
|
||||
- explicit canonical legacy subgroup와 old alias isolation, sample local/test explicit activation;
|
||||
- exact nine-card readiness registry. `filesystem-local-dev` managed single upload/download만 R1,
|
||||
나머지는 R0;
|
||||
- pinned AWS SDK `2.30.0` conditional/checksum/expected-owner API characterization과 explicit
|
||||
`NettyNioAsyncHttpClient` policy/factory/lifecycle;
|
||||
- exact AWS/MinIO binding, bounded async transport, managed S3 single/multipart/control primitives,
|
||||
response-loss resolution, selected-only provider composition;
|
||||
- durable direct single/multipart session and grant records, S3 presigner lifecycle, exact part
|
||||
acknowledgement, completion/abort fences, terminal exact-version replay;
|
||||
- digest-pinned MinIO contract/fault lanes and protected AWS compile-only lane. Exact MinIO는
|
||||
create-only PUT/complete 조건을 무시하므로 managed/direct mutation profile을
|
||||
`UNSUPPORTED`로 유지하며 readiness를 올리지 않는다.
|
||||
|
||||
증거는 [Batch A checkpoint](../../evidence/object-storage/batch-a-checkpoint.md),
|
||||
[Batch B checkpoint](../../evidence/object-storage/batch-b-checkpoint.md),
|
||||
[Batch C checkpoint](../../evidence/object-storage/batch-c-progress.md),
|
||||
[Batch D checkpoint](../../evidence/object-storage/batch-d-checkpoint.md),
|
||||
[Batch E pause checkpoint](../../evidence/object-storage/batch-e-pause-checkpoint.md)에 기록했다.
|
||||
|
||||
격리 브랜치에서는 private vault 경로를 잘못 선택해 capture가 차단됐지만, main 통합 작업은
|
||||
사용자가 지정한 public vault의
|
||||
`raw/branch-notes/chore-main-worktree-capability-integration.md`에 캡처했다.
|
||||
|
||||
legacy runtime 범위는 별도로 유지된다.
|
||||
|
||||
- `application-core`의 범용 `ObjectStoragePort`;
|
||||
- caller가 지정한 raw key에 `byte[]`를 put/get/delete/exists하는 계약;
|
||||
- local filesystem adapter;
|
||||
- synchronous AWS SDK v2 `S3Client`를 사용하는 S3/MinIO adapter;
|
||||
- 설정 한 개로 filesystem 또는 S3 backend를 선택하는 Spring composition;
|
||||
- 명시적 legacy-only filesystem 또는 S3 Spring composition;
|
||||
- filesystem unit test, mocked S3 unit test, Testcontainers MinIO integration test;
|
||||
- module registry가 허용한 `application-core`, `shared-contract` 의존성.
|
||||
|
||||
아직 구현되지 않은 범위:
|
||||
아직 구현되지 않은 runtime/provider 범위:
|
||||
|
||||
- streaming upload/download와 range read;
|
||||
- immutable object reference와 version token;
|
||||
- stable operation ID, request fingerprint, durable operation record;
|
||||
- conditional create/update/delete와 unknown-outcome reconciliation;
|
||||
- checksum 생성·전송·검증 계약;
|
||||
- metadata/head 계약;
|
||||
- staged upload, quarantine, scan, publication;
|
||||
- presigned upload/download;
|
||||
- multipart start/part/complete/abort/recovery;
|
||||
- public presigned upload/download endpoint와 inbound authorization;
|
||||
- R2 수준의 exhaustive multipart/direct response-loss·race·restart qualification;
|
||||
- lifecycle, versioning, retention, legal hold 검증;
|
||||
- production credential, encryption, TLS, expected-owner 정책;
|
||||
- bounded timeout, connection pool, retry amplification, graceful shutdown;
|
||||
- provider capability qualification과 exact readiness card;
|
||||
- multi-node linearizable control CAS와 production unknown-outcome reconciliation;
|
||||
- production credential, encryption, TLS, retention/Object Lock qualification;
|
||||
- orphan/abandoned multipart/retired object reaper;
|
||||
- database와 object storage 사이의 crash-safe workflow;
|
||||
- 운영 metric, trace, audit, runbook;
|
||||
- AWS sandbox fault/security evidence.
|
||||
|
||||
따라서 현재 MinIO round-trip test가 통과하더라도 S3 production readiness를 의미하지 않는다.
|
||||
이 문서의 상태가 “상세 설계 완료”인 것은 구현, R2 qualification 또는 운영 준비 완료를
|
||||
뜻하지 않는다.
|
||||
Batch B의 R1은 exact local functional topology 증거다. Multi-node CAS, power-loss durability,
|
||||
S3/MinIO qualification, R2 또는 운영 준비 완료를 뜻하지 않는다.
|
||||
|
||||
## 1. 설계 판정
|
||||
|
||||
@@ -3955,8 +4018,10 @@ authorization endpoint의 ephemeral response이며 stored object DTO에 영구
|
||||
|
||||
정본 구현 순서와 아래 결정의 현재 freeze/approval gate는
|
||||
[Object Storage Production Capability Implementation Plan](../plans/2026-07-28-objectstorage-production-capability.md)에
|
||||
기록한다. 계획 작성은 완료됐지만 모든 구현 task는 아직 미착수이며, 계획 승인이 public API,
|
||||
scanner provider 또는 AWS/IaC 외부 변경 권한을 자동으로 부여하지 않는다.
|
||||
기록한다. Tasks 1–24의 구현은 Batch E pause checkpoint까지 진행됐지만 Batch E 전체
|
||||
combined/root gate는 아직 통과하지 않았다. Tasks 25–30은 미착수다. 이 구현 진행이나
|
||||
계획 승인이 public API, scanner provider 또는 AWS/IaC 외부 변경 권한을 자동으로 부여하지
|
||||
않는다.
|
||||
|
||||
구현 계획은 다음 결정을 task 단위로 명시해야 한다.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user