67 lines
3.8 KiB
Markdown
67 lines
3.8 KiB
Markdown
# Fileserver Durable Recovery Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this
|
|
> plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. Repository policy is
|
|
> human-only, so no step stages or commits changes.
|
|
|
|
**Goal:** Make the local publication provider restart-safe for completed and sealed operations
|
|
without re-running the row producer.
|
|
|
|
**Architecture:** Keep the application port unchanged. The adapter owns a private operation journal
|
|
under `.ca-fileserver/operations`, writes records through forced temp files and atomic rename, and
|
|
uses a deterministic request fingerprint. A retry restores a verified terminal receipt or resumes a
|
|
sealed staged artifact; disagreement is a conflict or indeterminate outcome, never an overwrite.
|
|
|
|
**Tech Stack:** Java 21 NIO, JUnit 5, AssertJ, existing Gradle quality gates.
|
|
|
|
---
|
|
|
|
### Task 1: Define deterministic journal records and request fingerprints
|
|
|
|
**Files:**
|
|
- Create: `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPublicationJournalRecord.java`
|
|
- Create: `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPublicationJournalCodec.java`
|
|
- Create: `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilePublishRequestFingerprint.java`
|
|
- Test: `src/adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPublicationJournalTest.java`
|
|
|
|
- [x] Write a failing test proving stable request fingerprints and different fingerprints for
|
|
source/schema changes.
|
|
- [x] Write a failing test proving journal round-trip and rejection of corrupt/newer records.
|
|
- [x] Run
|
|
`./gradlew :adapter:outbound:fileserver:test --tests '*LocalPublicationJournalTest' --console=plain`
|
|
and confirm the missing types fail compilation.
|
|
- [x] Implement a bounded flat JSON codec with schema version, state, fingerprint, locator token,
|
|
checksum/counts and receipt snapshot fields. It must reject duplicate/unknown keys and never
|
|
serialize absolute paths or row data.
|
|
- [x] Run the focused test and confirm GREEN.
|
|
|
|
### Task 2: Add forced atomic journal persistence and recovery
|
|
|
|
**Files:**
|
|
- Create: `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPublicationJournal.java`
|
|
- Modify: `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalFilePublicationAdapter.java`
|
|
- Test: `src/adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalFilePublicationRecoveryTest.java`
|
|
|
|
- [x] Write a failing test where a completed operation is retried with a producer that throws; the
|
|
original receipt must be returned and the producer must remain uncalled.
|
|
- [x] Write a failing test that reconstructs a new adapter over a sealed journal plus staged bytes
|
|
and resumes publication without calling the producer.
|
|
- [x] Write a failing test proving the same operation ID with a different request is a conflict and
|
|
a digest mismatch is indeterminate.
|
|
- [x] Run the recovery test and confirm RED.
|
|
- [x] Persist `WRITING`, `SEALED`, and `PUBLISHED` records with temp + force + atomic move. Verify
|
|
the target size and SHA-256 before terminal reconstruction.
|
|
- [x] Run all Fileserver tests and confirm GREEN.
|
|
|
|
### Task 3: Report the exact readiness boundary
|
|
|
|
**Files:**
|
|
- Modify: `src/adapter/outbound/fileserver/README.md`
|
|
- Modify: `src/adapter/outbound/fileserver/CLAUDE.md`
|
|
- Modify: `docs/superpowers/specs/2026-07-26-fileserver-production-capability-design.md`
|
|
|
|
- [x] Mark single-node local restart recovery as implemented.
|
|
- [x] Keep multi-node fencing, bounded background reaper, SFTP, NFS and HA evidence explicitly
|
|
unimplemented.
|
|
- [x] Run `./gradlew :adapter:outbound:fileserver:check --console=plain`.
|