Skip to content

feat(lifecycle): add immutable artifact-deletion receipt foundation v2 - #282

Closed
seonghobae wants to merge 5 commits into
fix/admin-endpoint-auth-cleanfrom
review/durable-deletion-receipt-ledger-v2
Closed

feat(lifecycle): add immutable artifact-deletion receipt foundation v2#282
seonghobae wants to merge 5 commits into
fix/admin-endpoint-auth-cleanfrom
review/durable-deletion-receipt-ledger-v2

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Objective

Part of #263, Slice B. Add only the durable RECEIPT_V1 artifact-deletion state-store foundation on the exact tenant-isolated administrator snapshot. HTTP mutation, cleanup execution, scheduling, signed-link revocation, and product-status semantics remain later slices.

Exact clean stack

Exact current head is 3396c03a458294175006efd0c9cea35d6911d55c on immutable parent snapshot 3ba53ac843685e483fdd3f8f060ba61825b9a798 through base branch snapshot/admin-endpoint-auth-3ba53ac.

The parent is the merge base. The slice changes exactly 12 foundation-scoped files and preserves the parent's fail-closed lifecycle-event tenant query: null and blank scoped tenant identifiers cannot infer buyer-demo. It changes no endpoint, controller, cleanup worker, scheduler, workflow, dependency, packaging, version, or release path.

Test-first provenance

  1. RED 96a09662eed2449baece2b76e3bdd3d5c6800300 defines deterministic receipt identity, append, replay, crash-tail, transition, and failure contracts.
  2. GREEN 8c803dafd14c1b6e7cac80b39e5c5a9915fc1cbd adds the versioned store, configuration, ADR, changelog, and implementation.
  3. RED 31200cf17c18104568f7cd4b22ef87b1c83a20a1 restores deterministic coverage for full failure/retry/completion replay, terminal completion, malformed versions, blank required tenants, inconsistent failure evidence, and immutable-identity mutation.
  4. GREEN 3396c03a458294175006efd0c9cea35d6911d55c removes a redundant initial-attempt replay branch while retaining constructor-level invariant enforcement and complete fail-closed replay validation.

Receipt and durability contract

Each immutable identity binds deletion request ID, tenant, permanently reserved conversion-job ID, exact artifact SHA-256, privacy-safe audit correlation, and request time. Exact duplicates are idempotent; same-job identity conflicts fail closed.

The monotonic lifecycle is:

DELETION_REQUESTED → METADATA_TOMBSTONED → ARTIFACT_CLEANUP_PENDING → ARTIFACT_CLEANUP_COMPLETED

with retry:

ARTIFACT_CLEANUP_PENDING → ARTIFACT_CLEANUP_FAILED → ARTIFACT_CLEANUP_PENDING.

Completion is terminal. Transition time cannot reverse. Attempt count and attempt time must coexist consistently and remain preserved through retry and completion. Failure evidence is restricted to controlled [a-z0-9_]{1,64} codes.

Strict UTF-8 RECEIPT_V1 records are bounded to 16 KiB. Append uses a fixed LF commit delimiter and returns only after FileChannel.force(true). Any non-empty unterminated final tail fails closed and is preserved. Replay rejects malformed Base64URL, blank or missing identity, invalid timestamps, states, counts, immutable conflicts, non-monotonic time, illegal successors, unsafe failure details, and oversized records. Completed receipts remain retained for idempotency but are excluded from pending work. Standalone in-memory and restart-replay file adapters implement one ArtifactDeletionReceiptStore MSA contract.

Exact-head acceptance state

For exact current head 3396c03a458294175006efd0c9cea35d6911d55c:

  • fuzz run 31102838777 completed successfully across TenantClaimsFuzzTest, DocumentValidationFuzzTest, and ArtifactTokenParserFuzzTest after each job verified the exact checked-out SHA under Java 21;
  • crash-artifact upload steps were skipped only because no reproducer existed;
  • exact-head CodeRabbit commit status is successful, but status-only evidence is not a formal PR review or counted approval;
  • zero actionable unresolved inline review thread exists;
  • formal GitHub review submissions are zero;
  • formal exact-current-head CodeRabbit/OpenCode/Noema/Strix reviews are absent;
  • CI, Security Scan, SAST, complete Maven/JaCoCo/Javadoc evidence, and counted independent write-authorized approval are absent for this stacked base and therefore not passing.

Queued, pending, cancelled, skipped-required, absent-required, stale-head, predecessor-head, advisory-only, local-only, or synthetic-only evidence is not passing.

PR #280 was closed unmerged as superseded; none of its checks or reviews are transferred. Keep this PR Draft. Parent order remains #270#268 → this foundation → cleanup worker #283. This foundation alone does not resolve incomplete artifact cleanup or prove physical deletion completion. Do not weaken tests, infer approval from status-only evidence, bypass protections, or publish a release.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 76ec59df-277d-46c8-90f0-61ff92146342

📥 Commits

Reviewing files that changed from the base of the PR and between 19fbfc4 and 2d778cd.

📒 Files selected for processing (12)
  • CHANGELOG.md
  • docs/security/2026-08-06-durable-artifact-deletion-receipts.md
  • src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedger.java
  • src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceipt.java
  • src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceiptStore.java
  • src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionState.java
  • src/main/resources/application.yml
  • src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCoverageTest.java
  • src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCrashTailTest.java
  • src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayCoverageTest.java
  • src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayValidationTest.java
  • src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerTest.java

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@opencode-agent @cwl-noema-review Please independently review exact current head 8c803dafd14c1b6e7cac80b39e5c5a9915fc1cbd against exact parent snapshot 3ba53ac843685e483fdd3f8f060ba61825b9a798. Verify the bounded 2-commit/11-file receipt foundation, RED-before-GREEN provenance, strict UTF-8/16 KiB bounds, fixed LF commit delimiter, FileChannel.force(true), torn-tail fail-closed replay, immutable identity, attempt evidence, legal/illegal transitions, completed-receipt retention, standalone/file-backed MSA contract, parent tenant-lifecycle isolation, and absence of endpoint, cleanup worker, scheduler, workflow, dependency, packaging, version, or release changes. Do not approve or claim passing evidence until exact-head checks and every repository protection are satisfied.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

@seonghobae I will review PR #282 against 3ba53ac843685e483fdd3f8f060ba61825b9a798 at exact head 8c803dafd14c1b6e7cac80b39e5c5a9915fc1cbd.

I will not approve the PR or claim passing checks until the required exact-head evidence and repository protections are available.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@opencode-agent @cwl-noema-review Please independently review exact current head 3396c03a458294175006efd0c9cea35d6911d55c against exact parent snapshot 3ba53ac843685e483fdd3f8f060ba61825b9a798. Verify the four-commit RED/GREEN sequence and exact 12-file foundation: immutable receipt identity, constructor and replay invariants, full failure/retry/completion replay coverage, strict UTF-8/16 KiB bounds, fixed LF commit delimiter, FileChannel.force(true), torn-tail fail-closed replay, legal transitions, attempt evidence, completed-receipt retention, standalone/file-backed MSA contract, inherited tenant-lifecycle isolation, and absence of endpoint, cleanup worker, scheduler, workflow, dependency, packaging, version, or release changes. Do not approve or claim passing evidence until exact-head checks and every repository protection are complete.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

@seonghobae I will review PR #282 at exact head 3396c03a458294175006efd0c9cea35d6911d55c against parent snapshot 3ba53ac843685e483fdd3f8f060ba61825b9a798.

I will verify the specified receipt-store contracts, four-commit RED/GREEN sequence, 12-file scope, inherited tenant isolation, and absence of out-of-scope changes. I will not approve the PR or claim passing evidence unless the required exact-head checks and repository protections are complete.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai @opencode-agent @cwl-noema-review Please perform a fresh formal review of exact head 3396c03a458294175006efd0c9cea35d6911d55c on immutable administrator snapshot 3ba53ac843685e483fdd3f8f060ba61825b9a798. Verify immutable tenant/job/digest/audit identity, legal monotonic receipt transitions, strict bounded UTF-8 replay, malformed/truncated-tail fail-closed behavior, FileChannel.force(true) durability, idempotent duplicates, conflicting lifecycle rejection, retry evidence consistency, terminal completion, and standalone/MSA adapter boundaries. Exact-head fuzz 31102838777 succeeded; CI, Security Scan, SAST, Strix, complete Maven/JaCoCo/Javadoc evidence, and independent approval remain absent and must not be inferred. Submit current-head actionable findings or a formal review; a separate counted write-authorized independent approval remains mandatory.

@seonghobae
seonghobae changed the base branch from snapshot/admin-endpoint-auth-3ba53ac to fix/admin-endpoint-auth-clean August 6, 2026 13:08

Copy link
Copy Markdown
Collaborator Author

Closing unmerged as superseded. The exact receipt-foundation history and tree are now fast-forward integrated into PR #268 exact head 0aec05655849943887aec232c3107950159c741f, together with the dependent durable-cleanup worker. Keeping this separate Draft would duplicate the same changes and split exact-head review evidence. No prior #282 checks, reviews, or approval are transferred; #268 must rerun and satisfy every protection on its new exact head.

@seonghobae seonghobae closed this Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='3396c03a458294175006efd0c9cea35d6911d55c'
parent='3ba53ac843685e483fdd3f8f060ba61825b9a798'

printf '%s\n' '== Checked-out commit and requested commits =='
git rev-parse HEAD
git cat-file -t "$head"
git cat-file -t "$parent"
printf 'merge-base: '
git merge-base "$head" "$parent"

printf '%s\n' '== Commit sequence =='
git log --reverse --format='%H %s' "$parent..$head"

printf '%s\n' '== Changed files and diff stat =='
git diff --stat "$parent" "$head"
git diff --name-status "$parent" "$head"

printf '%s\n' '== Repository status =='
git status --short

printf '%s\n' '== Java source and test structure for receipt symbols =='
rg -n -i -C 2 'ArtifactDeletionReceipt(Store|File|Memory)?|RECEIPT_V1|FileChannel\.force|DELETION_REQUESTED|ARTIFACT_CLEANUP' \
  -g '*.java' .

printf '%s\n' '== Changed-file outlines =='
while IFS= read -r path; do
  case "$path" in
    *.java)
      printf '\n--- %s ---\n' "$path"
      ast-grep outline "$path" --items all --view expanded || true
      ;;
  esac
done < <(git diff --name-only "$parent" "$head")

printf '%s\n' '== Parent tenant-lifecycle query evidence =='
rg -n -i -C 4 'buyer-demo|tenant.*lifecycle|lifecycle.*tenant|scopedTenant' -g '*.java' .

Length of output: 83722


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='3396c03a458294175006efd0c9cea35d6911d55c'
parent='3ba53ac843685e483fdd3f8f060ba61825b9a798'

show() {
  local path="$1"
  printf '\n===== %s =====\n' "$path"
  git show "${head}:${path}" | nl -ba
}

printf '%s\n' '== Exact-head implementation =='
show 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceipt.java'
show 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedger.java'
show 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceiptStore.java'
show 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionState.java'

printf '%s\n' '== Exact-head receipt test method map =='
for path in \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCoverageTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCrashTailTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayCoverageTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayValidationTest.java'
do
  printf '\n===== %s =====\n' "$path"
  git show "${head}:${path}" | rg -n -C 2 '`@Test`|void [A-Za-z0-9_]+' || true
done

printf '%s\n' '== Out-of-scope change check =='
git diff --name-only "$parent" "$head" | rg -n -i \
  '(^|/)(controller|endpoint|worker|scheduler|workflow|pom\.xml|build\.gradle|gradle\.properties|package\.json|Dockerfile|.*\.github/|.*\.yml)$' \
  || true

printf '%s\n' '== Parent tenant-isolation changes and matching source =='
git diff -U8 "$parent" "$head" -- \
  'src/main/java/**/*.java' | rg -n -C 5 'buyer-demo|tenant|lifecycle' || true
git grep -n -i -E 'buyer-demo|scopedTenant|tenant.*lifecycle|lifecycle.*tenant' "$parent" -- 'src/main/java/*.java' 'src/main/java/**/*.java' || true

Length of output: 2338


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='3396c03a458294175006efd0c9cea35d6911d55c'
parent='3ba53ac843685e483fdd3f8f060ba61825b9a798'

show_numbered() {
  local path="$1"
  printf '\n===== %s =====\n' "$path"
  git show "${head}:${path}" | awk '{printf "%5d  %s\n", NR, $0}'
}

printf '%s\n' '== Exact-head receipt model =='
show_numbered 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceipt.java'

printf '%s\n' '== Exact-head ledger adapter =='
show_numbered 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedger.java'

printf '%s\n' '== Exact-head store contract and states =='
show_numbered 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionReceiptStore.java'
show_numbered 'src/main/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionState.java'

printf '%s\n' '== Exact-head test names =='
for path in \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCoverageTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCrashTailTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayCoverageTest.java' \
  'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerReplayValidationTest.java'
do
  printf '\n===== %s =====\n' "$path"
  git show "${head}:${path}" | rg -n -C 1 '`@Test`|void [A-Za-z0-9_]+' || true
done

printf '%s\n' '== Exact range: potentially out-of-scope files =='
git diff --name-only "$parent" "$head" | rg -n -i \
  '(^|/)(controller|endpoint|worker|scheduler|workflow|pom\.xml|build\.gradle|gradle\.properties|package\.json|Dockerfile|.*\.github/|.*\.yml)$' \
  || true

printf '%s\n' '== Tenant-isolation source at the immutable parent =='
git grep -n -i -E 'buyer-demo|scopedTenant|tenant.*lifecycle|lifecycle.*tenant' "$parent" -- 'src/main/java' || true

Length of output: 49143


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='3396c03a458294175006efd0c9cea35d6911d55c'
parent='3ba53ac843685e483fdd3f8f060ba61825b9a798'

slice() {
  local object="$1"
  local path="$2"
  local first="$3"
  local last="$4"
  printf '\n===== %s:%s lines %s-%s =====\n' "$object" "$path" "$first" "$last"
  git show "${object}:${path}" | sed -n "${first},${last}p" | awk -v n="$first" '{printf "%5d  %s\n", n++, $0}'
}

printf '%s\n' '== CRLF replay behavior and test expectation =='
slice "$head" 'src/test/java/com/clearfolio/viewer/lifecycle/ArtifactDeletionLedgerCoverageTest.java' 195 225

printf '%s\n' '== ADR and changelog delimiter and replay contract =='
git show "${head}:docs/security/2026-08-06-durable-artifact-deletion-receipts.md" \
  | rg -n -i -C 4 'LF|CRLF|delimiter|UTF-8|tail|16 KiB|16KiB|replay'
git show "${head}:CHANGELOG.md" | tail -n 40

printf '%s\n' '== Exact configuration change =='
git diff -U12 "$parent" "$head" -- 'src/main/resources/application.yml'

printf '%s\n' '== Parent tenant lifecycle query implementation =='
git grep -n -E 'findLifecycleEventsByTenantId|scopedTenant' "$parent" -- 'src/main/java' 'src/test/java' || true
git show "${parent}:src/main/java/com/clearfolio/viewer/repository/InMemoryConversionJobRepository.java" \
  | sed -n '230,285p' | awk '{printf "%5d  %s\n", NR + 229, $0}'

printf '%s\n' '== Exact-head difference from requested commit is excluded from review =='
git log --oneline "${head}..HEAD"

Length of output: 16156


@seonghobae Formal review of exact head 3396c03a458294175006efd0c9cea35d6911d55c found one blocking issue.

P1 — Reject CRLF records during replay.
ArtifactDeletionLedger.replayBytes removes a trailing carriage return before parsing. This accepts CRLF records. ArtifactDeletionLedgerCoverageTest.strictUtf8CrLfAndBlankLineReplayAreHandledExplicitly explicitly expects this behavior.

The receipt contract requires a fixed ASCII LF (0x0A) commit delimiter and fixed host-independent LF record delimiters. A CRLF record is not in that format. Replay must fail closed instead of normalizing the extra \r. Remove the carriage-return normalization and change the test to assert rejection of CRLF input.

I also verified these items by static inspection of the exact Git object:

  • The range contains the stated four commits and 12 files.
  • Receipt identity includes request ID, tenant ID, job ID, artifact checksum, audit correlation ID, and request time.
  • The model enforces legal transitions, nondecreasing transition times, attempt evidence consistency, and terminal completion.
  • The file adapter uses strict UTF-8 decoding, a 16 KiB replay bound, append-only writes, and FileChannel.force(true).
  • Replay rejects a non-empty unterminated final tail.
  • Completed receipts remain stored but do not appear in pending work.
  • The parent tenant lifecycle query returns no events for null or blank tenant IDs.
  • I found no endpoint, cleanup worker, scheduler, workflow, dependency, packaging, version, or release changes in the exact range.

I did not approve this PR. The stated fuzz result does not replace the absent CI, Security Scan, SAST, complete Maven/JaCoCo/Javadoc evidence, or independent counted approval.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant