2026-07-08 03:02:50 +00:00
package checkpoint
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
coreworkspace "gitea.maximumdirect.net/eric/notarius/internal/core/workspace"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)
type WorkspaceLoader struct {
root string
identityDigest string
}
func NewWorkspaceLoader ( settings coreworkspace . Settings , identity coreworkspace . CheckpointIdentity ) ( pipeline . CheckpointLoader , error ) {
root , err := settings . CheckpointDirectory ( identity )
if err != nil {
return nil , err
}
if strings . TrimSpace ( root ) == "" {
return pipeline . NoopCheckpointLoader ( ) , nil
}
return & WorkspaceLoader { root : root , identityDigest : identity . Digest } , nil
}
func ( l * WorkspaceLoader ) Enabled ( ) bool {
return l != nil && strings . TrimSpace ( l . root ) != ""
}
func ( l * WorkspaceLoader ) Source ( moduleKey string ) ( pipeline . SourceCheckpoint , pipeline . CheckpointDecision ) {
var manifest coreworkspace . SourceManifest
if decision := l . readJSON ( "source/manifest.json" , & manifest ) ; ! decision . Reused {
return pipeline . SourceCheckpoint { } , decision
}
if decision := l . validateManifest ( manifest . StageManifest , coreworkspace . StageSource , "" , moduleKey , coreworkspace . StatusSucceeded , nil ) ; ! decision . Reused {
return pipeline . SourceCheckpoint { } , decision
}
var payload sourceDocumentEnvelope
if decision := l . readJSON ( "source/source-document.json" , & payload ) ; ! decision . Reused {
return pipeline . SourceCheckpoint { } , decision
}
doc := cloneSourceDocument ( payload . Document )
if err := source . ValidateDocument ( & doc ) ; err != nil {
return pipeline . SourceCheckpoint { } , invalidDecision ( "source checkpoint document is invalid: %v" , err )
}
if strings . TrimSpace ( manifest . SourceID ) != "" && manifest . SourceID != doc . ID {
return pipeline . SourceCheckpoint { } , invalidDecision ( "source checkpoint source id does not match payload" )
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . OutputDigests ) , digestFingerprints ( "source_document" , doc . Digest ) ) {
return pipeline . SourceCheckpoint { } , invalidDecision ( "source checkpoint output digest does not match payload" )
}
return pipeline . SourceCheckpoint { Document : & doc } , reusedDecision ( )
}
func ( l * WorkspaceLoader ) Chunk ( moduleKey string , sourceDigest string ) ( pipeline . ChunkCheckpoint , pipeline . CheckpointDecision ) {
expectedDependencies := digestFingerprints ( "source_document" , sourceDigest )
var manifest coreworkspace . ChunkManifest
if decision := l . readJSON ( "chunk/manifest.json" , & manifest ) ; ! decision . Reused {
return pipeline . ChunkCheckpoint { } , decision
}
if decision := l . validateManifest ( manifest . StageManifest , coreworkspace . StageChunk , "" , moduleKey , coreworkspace . StatusSucceeded , expectedDependencies ) ; ! decision . Reused {
return pipeline . ChunkCheckpoint { } , decision
}
var payload chunksEnvelope
if decision := l . readJSON ( "chunk/chunks.json" , & payload ) ; ! decision . Reused {
return pipeline . ChunkCheckpoint { } , decision
}
chunks , err := sourceChunksFromEnvelope ( payload . Chunks )
if err != nil {
return pipeline . ChunkCheckpoint { } , invalidDecision ( "chunk checkpoint payload is invalid: %v" , err )
}
if len ( chunks ) == 0 {
return pipeline . ChunkCheckpoint { } , invalidDecision ( "chunk checkpoint payload has no chunks" )
}
2026-07-17 05:32:11 +00:00
outputDigests , err := chunkOutputDigests ( chunks )
if err != nil {
return pipeline . ChunkCheckpoint { } , invalidDecision ( "chunk checkpoint output cannot be digested: %v" , err )
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . OutputDigests ) , outputDigests ) {
2026-07-08 03:02:50 +00:00
return pipeline . ChunkCheckpoint { } , invalidDecision ( "chunk checkpoint output digests do not match payload" )
}
return pipeline . ChunkCheckpoint { Chunks : chunks , Warnings : cloneWarnings ( payload . Warnings ) } , reusedDecision ( )
}
2026-07-17 08:13:08 +00:00
func ( l * WorkspaceLoader ) Extract ( laneID , moduleKey string , dependencies [ ] pipeline . CheckpointFingerprint ) ( pipeline . ExtractCheckpoint , pipeline . CheckpointDecision ) {
2026-07-17 07:33:49 +00:00
var manifest coreworkspace . ExtractLaneManifest
if d := l . readJSON ( laneManifestPath ( "extract" , laneID ) , & manifest ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
if d := l . validateLaneManifest ( manifest . StageManifest , coreworkspace . StageExtract , laneID , moduleKey , dependencies , coreworkspace . StatusSucceeded , coreworkspace . StatusSucceededWithRejections ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
var payload artifactExtractEnvelope
if d := l . readJSON ( lanePayloadPath ( "extract" , laneID , "outputs.json" ) , & payload ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
outputs , err := artifactCheckpointOutputs ( payload . Outputs )
if err != nil {
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { } , invalidDecision ( "extract artifact checkpoint payload is invalid: %v" , err )
2026-07-17 07:33:49 +00:00
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . OutputDigests ) , artifactOutputDigests ( outputs ) ) {
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { } , invalidDecision ( "extract artifact checkpoint output digests do not match payload" )
2026-07-08 03:02:50 +00:00
}
2026-07-17 08:13:08 +00:00
return pipeline . ExtractCheckpoint { Outputs : outputs , Rejected : cloneRejectedOutputs ( payload . Rejected ) , Warnings : cloneWarnings ( payload . Warnings ) } , reusedDecision ( )
2026-07-08 03:02:50 +00:00
}
2026-07-17 08:13:08 +00:00
func ( l * WorkspaceLoader ) Merge ( laneID , moduleKey string , dependencies [ ] pipeline . CheckpointFingerprint ) ( pipeline . MergeCheckpoint , pipeline . CheckpointDecision ) {
2026-07-17 07:33:49 +00:00
var manifest coreworkspace . MergeLaneManifest
if d := l . readJSON ( laneManifestPath ( "merge" , laneID ) , & manifest ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
if d := l . validateLaneManifest ( manifest . StageManifest , coreworkspace . StageMerge , laneID , moduleKey , dependencies , coreworkspace . StatusSucceeded ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
var payload artifactSingleEnvelope
if d := l . readJSON ( lanePayloadPath ( "merge" , laneID , "output.json" ) , & payload ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
values , err := artifactCheckpointOutputs ( [ ] artifactCheckpointEnvelope { payload . Output } )
if err != nil {
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { } , invalidDecision ( "merge artifact checkpoint payload is invalid: %v" , err )
2026-07-17 07:33:49 +00:00
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . OutputDigests ) , artifactOutputDigests ( values ) ) {
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { } , invalidDecision ( "merge artifact checkpoint output digest does not match payload" )
2026-07-08 03:02:50 +00:00
}
2026-07-17 08:13:08 +00:00
return pipeline . MergeCheckpoint { Output : values [ 0 ] , Warnings : cloneWarnings ( payload . Warnings ) } , reusedDecision ( )
2026-07-08 03:02:50 +00:00
}
2026-07-17 08:13:08 +00:00
func ( l * WorkspaceLoader ) Normalize ( laneID , moduleKey string , dependencies [ ] pipeline . CheckpointFingerprint ) ( pipeline . NormalizeCheckpoint , pipeline . CheckpointDecision ) {
2026-07-17 07:33:49 +00:00
var manifest coreworkspace . NormalizeLaneManifest
if d := l . readJSON ( laneManifestPath ( "normalize" , laneID ) , & manifest ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
if d := l . validateLaneManifest ( manifest . StageManifest , coreworkspace . StageNormalize , laneID , moduleKey , dependencies , coreworkspace . StatusSucceeded ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
var payload artifactSingleEnvelope
if d := l . readJSON ( lanePayloadPath ( "normalize" , laneID , "output.json" ) , & payload ) ; ! d . Reused {
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { } , d
2026-07-17 07:33:49 +00:00
}
values , err := artifactCheckpointOutputs ( [ ] artifactCheckpointEnvelope { payload . Output } )
if err != nil {
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { } , invalidDecision ( "normalize artifact checkpoint payload is invalid: %v" , err )
2026-07-17 07:33:49 +00:00
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . OutputDigests ) , artifactOutputDigests ( values ) ) {
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { } , invalidDecision ( "normalize artifact checkpoint output digest does not match payload" )
2026-07-17 07:33:49 +00:00
}
2026-07-17 08:13:08 +00:00
return pipeline . NormalizeCheckpoint { Output : values [ 0 ] , Warnings : cloneWarnings ( payload . Warnings ) } , reusedDecision ( )
2026-07-17 07:33:49 +00:00
}
2026-07-17 08:13:08 +00:00
func artifactCheckpointOutputs ( values [ ] artifactCheckpointEnvelope ) ( [ ] pipeline . CheckpointArtifact , error ) {
2026-07-17 07:33:49 +00:00
if len ( values ) == 0 {
return nil , nil
}
2026-07-17 08:13:08 +00:00
out := make ( [ ] pipeline . CheckpointArtifact , 0 , len ( values ) )
2026-07-17 07:33:49 +00:00
for _ , v := range values {
content , err := contentFromEnvelope ( v . Content )
if err != nil {
return nil , err
}
if strings . TrimSpace ( string ( v . Kind ) ) == "" || strings . TrimSpace ( v . Schema . ID ) == "" || strings . TrimSpace ( v . Schema . Version ) == "" || strings . TrimSpace ( v . SchemaDigest ) == "" {
return nil , fmt . Errorf ( "artifact codec identity is incomplete" )
}
2026-07-17 08:13:08 +00:00
out = append ( out , pipeline . CheckpointArtifact { LaneID : v . LaneID , ModuleKey : v . ModuleKey , SourceID : v . SourceID , ChunkID : v . ChunkID , ChunkIndex : v . ChunkIndex , ChunkRef : v . ChunkRef , SchemaDigest : v . SchemaDigest , Artifact : contracts . SerializedArtifact { Kind : v . Kind , Schema : v . Schema , MediaType : v . Content . MediaType , Content : content , Metadata : cloneMetadata ( v . Content . Metadata ) } } )
2026-07-17 07:33:49 +00:00
}
return out , nil
}
2026-07-08 03:02:50 +00:00
func ( l * WorkspaceLoader ) readJSON ( name string , out any ) pipeline . CheckpointDecision {
if ! l . Enabled ( ) {
return pipeline . CheckpointDecision { Reason : "checkpoint loading disabled" }
}
target , err := coreworkspace . SafePath ( l . root , name )
if err != nil {
return invalidDecision ( "checkpoint path is invalid: %v" , err )
}
data , err := os . ReadFile ( target )
if err != nil {
if os . IsNotExist ( err ) {
return pipeline . CheckpointDecision { Reason : "checkpoint artifact is missing" }
}
return invalidDecision ( "read checkpoint artifact: %v" , err )
}
if err := json . Unmarshal ( data , out ) ; err != nil {
return invalidDecision ( "decode checkpoint artifact: %v" , err )
}
return reusedDecision ( )
}
func ( l * WorkspaceLoader ) validateManifest ( manifest coreworkspace . StageManifest , stage coreworkspace . StageName , laneID string , moduleKey string , status coreworkspace . StageStatus , dependencies [ ] pipeline . CheckpointFingerprint ) pipeline . CheckpointDecision {
return l . validateLaneManifest ( manifest , stage , laneID , moduleKey , dependencies , status )
}
func ( l * WorkspaceLoader ) validateLaneManifest ( manifest coreworkspace . StageManifest , stage coreworkspace . StageName , laneID string , moduleKey string , dependencies [ ] pipeline . CheckpointFingerprint , statuses ... coreworkspace . StageStatus ) pipeline . CheckpointDecision {
2026-07-17 05:32:11 +00:00
if manifest . WorkspaceSchemaVersion == coreworkspace . WorkspaceSchemaVersionV1 {
return invalidDecision ( "checkpoint workspace schema version %q is incompatible with %q and must be recomputed" , manifest . WorkspaceSchemaVersion , coreworkspace . WorkspaceSchemaVersion )
}
2026-07-08 03:02:50 +00:00
if manifest . WorkspaceSchemaVersion != coreworkspace . WorkspaceSchemaVersion {
return invalidDecision ( "checkpoint workspace schema version %q is not supported" , manifest . WorkspaceSchemaVersion )
}
if strings . TrimSpace ( l . identityDigest ) != "" && manifest . Metadata [ "checkpoint_identity_digest" ] != l . identityDigest {
return invalidDecision ( "checkpoint identity digest does not match current invocation" )
}
if manifest . Stage != stage {
return invalidDecision ( "checkpoint stage %q does not match %q" , manifest . Stage , stage )
}
if strings . TrimSpace ( laneID ) != "" && manifest . LaneID != laneID {
return invalidDecision ( "checkpoint lane %q does not match %q" , manifest . LaneID , laneID )
}
if strings . TrimSpace ( moduleKey ) != "" && manifest . ModuleKey != moduleKey {
return invalidDecision ( "checkpoint module %q does not match %q" , manifest . ModuleKey , moduleKey )
}
statusOK := false
for _ , status := range statuses {
if manifest . Status == status {
statusOK = true
break
}
}
if ! statusOK {
return invalidDecision ( "checkpoint status %q cannot be reused" , manifest . Status )
}
if ! fingerprintsEqual ( coreworkspaceToPipelineFingerprints ( manifest . DependencyFingerprints ) , dependencies ) {
return invalidDecision ( "checkpoint dependency fingerprints do not match" )
}
return reusedDecision ( )
}
2026-07-17 05:32:11 +00:00
func sourceChunksFromEnvelope ( values [ ] chunkEnvelope ) ( [ ] source . Chunk , error ) {
2026-07-08 03:02:50 +00:00
if len ( values ) == 0 {
return nil , nil
}
2026-07-17 05:32:11 +00:00
out := make ( [ ] source . Chunk , 0 , len ( values ) )
2026-07-08 03:02:50 +00:00
for _ , value := range values {
content , err := contentFromEnvelope ( value . Content )
if err != nil {
return nil , err
}
2026-07-17 23:47:22 +00:00
annotations , err := source . CanonicalizeChunkAnnotations ( value . Annotations )
if err != nil {
return nil , fmt . Errorf ( "canonicalize chunk %q annotations: %w" , value . ID , err )
}
planAnnotations , err := source . CanonicalizeChunkAnnotations ( value . PlanAnnotations )
if err != nil {
return nil , fmt . Errorf ( "canonicalize chunk %q plan_annotations: %w" , value . ID , err )
}
2026-07-17 05:32:11 +00:00
out = append ( out , source . Chunk {
2026-07-17 23:47:22 +00:00
ID : value . ID ,
SourceID : value . SourceID ,
Index : value . Index ,
Ref : value . Ref ,
Content : content ,
MediaType : value . Content . MediaType ,
Units : cloneSourceUnits ( value . Units ) ,
Metadata : cloneMetadata ( value . Metadata ) ,
Annotations : annotations ,
PlanAnnotations : planAnnotations ,
2026-07-08 03:02:50 +00:00
} )
}
return out , nil
}
func contentFromEnvelope ( value binaryEnvelope ) ( [ ] byte , error ) {
content , err := base64 . StdEncoding . DecodeString ( value . ContentBase64 )
if err != nil {
return nil , fmt . Errorf ( "decode content_base64: %w" , err )
}
if digest := strings . TrimSpace ( value . ContentDigest ) ; digest != "" && digest != contentDigest ( content ) {
return nil , fmt . Errorf ( "content digest mismatch" )
}
return content , nil
}
func coreworkspaceToPipelineFingerprints ( values [ ] coreworkspace . Fingerprint ) [ ] pipeline . CheckpointFingerprint {
if len ( values ) == 0 {
return nil
}
out := make ( [ ] pipeline . CheckpointFingerprint , 0 , len ( values ) )
for _ , value := range values {
out = append ( out , pipeline . CheckpointFingerprint { Name : value . Name , Value : value . Value } )
}
return normalizeFingerprints ( out )
}
func fingerprintsEqual ( a [ ] pipeline . CheckpointFingerprint , b [ ] pipeline . CheckpointFingerprint ) bool {
a = normalizeFingerprints ( a )
b = normalizeFingerprints ( b )
if len ( a ) != len ( b ) {
return false
}
for i := range a {
if a [ i ] != b [ i ] {
return false
}
}
return true
}
func reusedDecision ( ) pipeline . CheckpointDecision {
return pipeline . CheckpointDecision { Reused : true , Reason : "checkpoint is valid" }
}
func invalidDecision ( format string , args ... any ) pipeline . CheckpointDecision {
return pipeline . CheckpointDecision { Reason : fmt . Sprintf ( format , args ... ) }
}