Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 281c2a8a94 | |||
| 57a691236e | |||
| 858bf67043 |
@@ -22,7 +22,7 @@ Activate when the user:
|
||||
|
||||
## Core Function: The Seam Review Protocol
|
||||
|
||||
**Goal:** explain whether the code presents a reviewable bounded seam, why it is or is not working, and rank the improvements
|
||||
**Goal:** explain whether the code presents a reviewable bounded seam, why it is or is not working, and what the smallest high-value improvements would be.
|
||||
|
||||
**Required input:**
|
||||
|
||||
@@ -142,7 +142,7 @@ If naming is part of the problem:
|
||||
1. state why the current name is weak
|
||||
2. propose three alternatives when the name is important
|
||||
3. choose the option that best preserves caller intent across likely model evolution
|
||||
4. prefer intent-first names over vague ready/process labels when those better describe the protected invariant. follow the naming guidelines in "docs/explanation/naming-for-domain-modeling.md"
|
||||
4. prefer intent-first names such as accepted, confirmed, authorized, selected, or emitted outcomes over vague ready/process labels when those better describe the protected invariant
|
||||
|
||||
## Constraints
|
||||
|
||||
|
||||
@@ -1,13 +1,46 @@
|
||||
export * from "./models/shared.js"
|
||||
export * from "./models/types.js"
|
||||
export * from "./models/factories.js"
|
||||
export * from "./models/ops.js"
|
||||
export * from "./policies/selection.js"
|
||||
export * from "./policies/segments.js"
|
||||
|
||||
export type {
|
||||
Error,
|
||||
Event,
|
||||
IngestUpstreamSnapshot,
|
||||
State,
|
||||
UpstreamSnapshotIngested,
|
||||
SnapshotIngestHardStopped,
|
||||
AwaitingTrustedBundle,
|
||||
TrustedSnapshotSelected,
|
||||
IngestableSnapshot,
|
||||
} from "./models/types.js"
|
||||
export type {
|
||||
RunManifest,
|
||||
SegmentRecord,
|
||||
SelectedSnapshot,
|
||||
SnapshotIdentity,
|
||||
SnapshotMetadata,
|
||||
TaintedBundleLocation,
|
||||
TrustedBundleLocation,
|
||||
RunIdentity,
|
||||
} from "./models/shared.js"
|
||||
export {
|
||||
makeSnapshotIdentity,
|
||||
makeTaintedBundleInput,
|
||||
makeTaintedBundleLocation,
|
||||
makeVerifiedPreviousRunManifest,
|
||||
} from "./models/factories.js"
|
||||
export {
|
||||
makeAstNodeKind,
|
||||
makeNormalizedHash,
|
||||
makeRawHash,
|
||||
makeRunIdentity,
|
||||
makeShapeHash,
|
||||
makeTrustedCanonicalProjectionPath,
|
||||
makeTrustedManifestPath,
|
||||
makeTrustedSegmentsPath,
|
||||
makeTrustedSummaryPath,
|
||||
} from "./models/ops.js"
|
||||
export { workflow } from "./workflows/ingestSnapshot.js"
|
||||
export {
|
||||
apply,
|
||||
decide,
|
||||
makeAwaitingSnapshotSelection,
|
||||
emitSnapshotIngested,
|
||||
makeAwaitingTrustedBundle,
|
||||
validatePreviousRunManifest,
|
||||
} from "./policies/decideSnapshotIngest.js"
|
||||
|
||||
@@ -1,47 +1,19 @@
|
||||
import type {
|
||||
DerivedRunIdentity,
|
||||
Error,
|
||||
IngestFailureReason,
|
||||
TaintedBundleInput,
|
||||
VerifiedPreviousRunManifest,
|
||||
} from "./types.js"
|
||||
import type {
|
||||
AstNodeKind,
|
||||
NormalizedHash,
|
||||
RawHash,
|
||||
RunIdentity,
|
||||
RunManifest,
|
||||
ShapeHash,
|
||||
SnapshotIdentity,
|
||||
TaintedBundleLocation,
|
||||
TrustedBundleLocation,
|
||||
TrustedCanonicalProjectionPath,
|
||||
TrustedManifestPath,
|
||||
TrustedSegmentsPath,
|
||||
TrustedSummaryPath,
|
||||
} from "./shared.js"
|
||||
|
||||
const asBrand = <T>(value: string): T => value as T
|
||||
|
||||
export const makeSnapshotIdentity = (value: string): SnapshotIdentity => asBrand(value)
|
||||
export const makeSnapshotIdentity = (value: string): SnapshotIdentity =>
|
||||
value as SnapshotIdentity
|
||||
export const makeTaintedBundleLocation = (value: string): TaintedBundleLocation =>
|
||||
asBrand(value)
|
||||
export const makeTrustedBundleLocation = (value: string): TrustedBundleLocation =>
|
||||
asBrand(value)
|
||||
export const makeRunIdentity = (value: string): RunIdentity => asBrand(value)
|
||||
export const makeAstNodeKind = (value: string): AstNodeKind => asBrand(value)
|
||||
export const makeRawHash = (value: string): RawHash => asBrand(value)
|
||||
export const makeNormalizedHash = (value: string): NormalizedHash => asBrand(value)
|
||||
export const makeShapeHash = (value: string): ShapeHash => asBrand(value)
|
||||
export const makeTrustedManifestPath = (value: string): TrustedManifestPath =>
|
||||
asBrand(value)
|
||||
export const makeTrustedSegmentsPath = (value: string): TrustedSegmentsPath =>
|
||||
asBrand(value)
|
||||
export const makeTrustedCanonicalProjectionPath = (
|
||||
value: string,
|
||||
): TrustedCanonicalProjectionPath => asBrand(value)
|
||||
export const makeTrustedSummaryPath = (value: string): TrustedSummaryPath =>
|
||||
asBrand(value)
|
||||
value as TaintedBundleLocation
|
||||
|
||||
export const makeVerifiedPreviousRunManifest = (
|
||||
manifest: RunManifest,
|
||||
@@ -51,10 +23,6 @@ export const makeTaintedBundleInput = (
|
||||
location: TaintedBundleLocation,
|
||||
): TaintedBundleInput => ({ _tag: "TaintedBundleInput", location })
|
||||
|
||||
export const makeDerivedRunIdentity = (
|
||||
value: RunIdentity,
|
||||
): DerivedRunIdentity => ({ _tag: "DerivedRunIdentity", value })
|
||||
|
||||
export const foldFailure = (
|
||||
snapshotIdentity: SnapshotIdentity,
|
||||
reason: IngestFailureReason,
|
||||
|
||||
@@ -2,36 +2,48 @@ import { Either } from "effect"
|
||||
|
||||
import {
|
||||
isNonEmptyString,
|
||||
type AstNodeKind,
|
||||
type NormalizedHash,
|
||||
type RawHash,
|
||||
type RunIdentity,
|
||||
type SegmentRecord,
|
||||
type SelectedSnapshot,
|
||||
type ShapeHash,
|
||||
type SnapshotIdentity,
|
||||
type TrustedBundleLocation,
|
||||
type TrustedCanonicalProjectionPath,
|
||||
type TrustedManifestPath,
|
||||
type TrustedSegmentsPath,
|
||||
type TrustedSummaryPath,
|
||||
} from "./shared.js"
|
||||
import {
|
||||
foldFailure,
|
||||
makeAstNodeKind,
|
||||
makeDerivedRunIdentity,
|
||||
makeNormalizedHash,
|
||||
makeRawHash,
|
||||
makeRunIdentity,
|
||||
makeShapeHash,
|
||||
makeTrustedBundleLocation,
|
||||
makeTrustedCanonicalProjectionPath,
|
||||
makeTrustedManifestPath,
|
||||
makeTrustedSegmentsPath,
|
||||
makeTrustedSummaryPath,
|
||||
} from "./factories.js"
|
||||
import { foldFailure } from "./factories.js"
|
||||
import type {
|
||||
DerivedRunIdentity,
|
||||
Error,
|
||||
IngestableSnapshot,
|
||||
RequiredArtifact,
|
||||
TaintedBundleInput,
|
||||
TrustedSnapshotSelected,
|
||||
} from "./types.js"
|
||||
|
||||
export const makeTrustedBundleLocation = (value: string): TrustedBundleLocation =>
|
||||
value as TrustedBundleLocation
|
||||
export const makeRunIdentity = (value: string): RunIdentity => value as RunIdentity
|
||||
export const makeAstNodeKind = (value: string): AstNodeKind => value as AstNodeKind
|
||||
export const makeRawHash = (value: string): RawHash => value as RawHash
|
||||
export const makeNormalizedHash = (value: string): NormalizedHash =>
|
||||
value as NormalizedHash
|
||||
export const makeShapeHash = (value: string): ShapeHash => value as ShapeHash
|
||||
export const makeTrustedManifestPath = (value: string): TrustedManifestPath =>
|
||||
value as TrustedManifestPath
|
||||
export const makeTrustedSegmentsPath = (value: string): TrustedSegmentsPath =>
|
||||
value as TrustedSegmentsPath
|
||||
export const makeTrustedCanonicalProjectionPath = (
|
||||
value: string,
|
||||
): TrustedCanonicalProjectionPath => value as TrustedCanonicalProjectionPath
|
||||
export const makeTrustedSummaryPath = (value: string): TrustedSummaryPath =>
|
||||
value as TrustedSummaryPath
|
||||
|
||||
const parseBundleLocationText = (location: string): string | null => {
|
||||
const trimmedLocation = location.trim()
|
||||
return trimmedLocation.length === 0 || !trimmedLocation.includes("/")
|
||||
@@ -63,19 +75,19 @@ const decideSegmentRecordFailure = (
|
||||
export const parseBundleLocation = (
|
||||
snapshotIdentity: SnapshotIdentity,
|
||||
input: TaintedBundleInput,
|
||||
): Either.Either<ReturnType<typeof makeTrustedBundleLocation>, Error> => {
|
||||
): Either.Either<TrustedBundleLocation, Error> => {
|
||||
const location = parseBundleLocationText(input.location as string)
|
||||
return location === null
|
||||
? Either.left(foldFailure(snapshotIdentity, "BundleNotParseable"))
|
||||
: Either.right(makeTrustedBundleLocation(location))
|
||||
}
|
||||
|
||||
export const applyRunIdentityRules = (
|
||||
const deriveRunIdentity = (
|
||||
selectedSnapshot: SelectedSnapshot,
|
||||
): Either.Either<DerivedRunIdentity, Error> => {
|
||||
const snapshotIdentity = selectedSnapshot.SnapshotIdentity as string
|
||||
return isNonEmptyString(snapshotIdentity)
|
||||
? Either.right(makeDerivedRunIdentity(makeRunIdentity(`run:${snapshotIdentity}`)))
|
||||
? Either.right({ _tag: "DerivedRunIdentity", value: makeRunIdentity(`run:${snapshotIdentity}`) })
|
||||
: Either.left(
|
||||
foldFailure(
|
||||
selectedSnapshot.SnapshotIdentity,
|
||||
@@ -136,6 +148,37 @@ export const validateRequiredArtifacts = (
|
||||
)
|
||||
}
|
||||
|
||||
export const decideIngestableSnapshot = (
|
||||
trustedSnapshot: TrustedSnapshotSelected,
|
||||
): Either.Either<IngestableSnapshot, Error> =>
|
||||
Either.flatMap(deriveRunIdentity(trustedSnapshot.SelectedSnapshot), (derivedRunIdentity) =>
|
||||
Either.flatMap(validateSegmentRecords(trustedSnapshot.SelectedSnapshot), (segmentRecords) =>
|
||||
Either.flatMap(
|
||||
validateBoundaryProofs(
|
||||
trustedSnapshot.SelectedSnapshot.SnapshotIdentity,
|
||||
segmentRecords,
|
||||
),
|
||||
(boundaryProofs) =>
|
||||
Either.map(
|
||||
validateRequiredArtifacts(
|
||||
trustedSnapshot.SelectedSnapshot.SnapshotIdentity,
|
||||
trustedSnapshot.RequiredArtifacts,
|
||||
segmentRecords,
|
||||
),
|
||||
() => ({
|
||||
_tag: "IngestableSnapshot" as const,
|
||||
RunIdentity: derivedRunIdentity.value,
|
||||
SelectedSnapshot: trustedSnapshot.SelectedSnapshot,
|
||||
PreviousRunManifest: trustedSnapshot.PreviousRunManifest,
|
||||
SegmentRecords: segmentRecords,
|
||||
BoundaryProofs: boundaryProofs,
|
||||
RequiredArtifacts: trustedSnapshot.RequiredArtifacts,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
export const deriveRequiredArtifactPaths = (
|
||||
runIdentity: RunIdentity,
|
||||
): {
|
||||
|
||||
@@ -68,8 +68,8 @@ export type Error = {
|
||||
readonly payload: SnapshotIngestHardStopped
|
||||
}
|
||||
|
||||
export type AwaitingSnapshotSelection = {
|
||||
readonly _tag: "AwaitingSnapshotSelection"
|
||||
export type AwaitingTrustedBundle = {
|
||||
readonly _tag: "AwaitingTrustedBundle"
|
||||
readonly RunIdentityRulesDescription: string
|
||||
readonly BoundaryRulesDescription: string
|
||||
readonly RequiredArtifacts: ReadonlyArray<RequiredArtifact>
|
||||
@@ -77,8 +77,8 @@ export type AwaitingSnapshotSelection = {
|
||||
readonly ParseBudget: number
|
||||
}
|
||||
|
||||
export type SnapshotReady = {
|
||||
readonly _tag: "SnapshotReady"
|
||||
export type TrustedSnapshotSelected = {
|
||||
readonly _tag: "TrustedSnapshotSelected"
|
||||
readonly SelectedSnapshot: SelectedSnapshot
|
||||
readonly PreviousRunManifest: VerifiedPreviousRunManifest | null
|
||||
readonly RequiredArtifacts: ReadonlyArray<RequiredArtifact>
|
||||
@@ -86,8 +86,8 @@ export type SnapshotReady = {
|
||||
readonly ParseBudget: number
|
||||
}
|
||||
|
||||
export type DeterministicSegmentsReady = {
|
||||
readonly _tag: "DeterministicSegmentsReady"
|
||||
export type IngestableSnapshot = {
|
||||
readonly _tag: "IngestableSnapshot"
|
||||
readonly RunIdentity: RunIdentity
|
||||
readonly SelectedSnapshot: SelectedSnapshot
|
||||
readonly PreviousRunManifest: VerifiedPreviousRunManifest | null
|
||||
@@ -97,7 +97,7 @@ export type DeterministicSegmentsReady = {
|
||||
}
|
||||
|
||||
export type State =
|
||||
| AwaitingSnapshotSelection
|
||||
| SnapshotReady
|
||||
| DeterministicSegmentsReady
|
||||
| AwaitingTrustedBundle
|
||||
| TrustedSnapshotSelected
|
||||
| IngestableSnapshot
|
||||
| ({ readonly _tag: "SnapshotIngested" } & UpstreamSnapshotIngested)
|
||||
|
||||
@@ -1,31 +1,67 @@
|
||||
import { Either } from "effect"
|
||||
|
||||
import { foldFailure, makeVerifiedPreviousRunManifest } from "../models/factories.js"
|
||||
import {
|
||||
type AwaitingSnapshotSelection,
|
||||
type DeterministicSegmentsReady,
|
||||
type Event,
|
||||
type IngestUpstreamSnapshot,
|
||||
type RunManifest,
|
||||
type State,
|
||||
type UpstreamSnapshotIngested,
|
||||
decideIngestableSnapshot,
|
||||
deriveRequiredArtifactPaths,
|
||||
} from "../index.js"
|
||||
import { decideSegmentRecords } from "./segments.js"
|
||||
import {
|
||||
validatePreviousRunManifest,
|
||||
validateSnapshotSelection,
|
||||
} from "./selection.js"
|
||||
parseBundleLocation,
|
||||
} from "../models/ops.js"
|
||||
import type {
|
||||
AwaitingTrustedBundle,
|
||||
Error,
|
||||
Event,
|
||||
IngestableSnapshot,
|
||||
IngestUpstreamSnapshot,
|
||||
State,
|
||||
TrustedSnapshotSelected,
|
||||
UpstreamSnapshotIngested,
|
||||
} from "../models/types.js"
|
||||
import type { RunManifest } from "../models/shared.js"
|
||||
|
||||
const toEvent = (
|
||||
deterministicSegmentsReady: DeterministicSegmentsReady,
|
||||
): Event => {
|
||||
const artifactPaths = deriveRequiredArtifactPaths(
|
||||
deterministicSegmentsReady.RunIdentity,
|
||||
export const validatePreviousRunManifest = (
|
||||
manifest: RunManifest,
|
||||
): Either.Either<ReturnType<typeof makeVerifiedPreviousRunManifest>, Error> =>
|
||||
manifest.ManifestPath && manifest.SegmentsPath && manifest.CanonicalProjectionPath
|
||||
? Either.right(makeVerifiedPreviousRunManifest(manifest))
|
||||
: Either.left(
|
||||
foldFailure(manifest.SnapshotIdentity, "PreviousRunManifestNotVerified"),
|
||||
)
|
||||
|
||||
const selectTrustedSnapshot = (
|
||||
state: State,
|
||||
command: IngestUpstreamSnapshot,
|
||||
): Either.Either<TrustedSnapshotSelected, Error> => {
|
||||
if (state._tag !== "AwaitingTrustedBundle") {
|
||||
return Either.left(
|
||||
foldFailure(command.SnapshotIdentity, "RunIdentityCouldNotBeDerived"),
|
||||
)
|
||||
}
|
||||
|
||||
return Either.map(
|
||||
parseBundleLocation(command.SnapshotIdentity, command.BundleInput),
|
||||
(bundleLocation) => ({
|
||||
_tag: "TrustedSnapshotSelected" as const,
|
||||
SelectedSnapshot: {
|
||||
SnapshotIdentity: command.SnapshotIdentity,
|
||||
BundleLocation: bundleLocation,
|
||||
SnapshotMetadata: command.SnapshotMetadata,
|
||||
},
|
||||
PreviousRunManifest: command.PreviousRunManifest,
|
||||
RequiredArtifacts: state.RequiredArtifacts,
|
||||
MaxBundleBytes: state.MaxBundleBytes,
|
||||
ParseBudget: state.ParseBudget,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export const emitSnapshotIngested = (
|
||||
ingestableSnapshot: IngestableSnapshot,
|
||||
): Event => {
|
||||
const artifactPaths = deriveRequiredArtifactPaths(ingestableSnapshot.RunIdentity)
|
||||
|
||||
const runManifest: RunManifest = {
|
||||
RunIdentity: deterministicSegmentsReady.RunIdentity,
|
||||
SnapshotIdentity: deterministicSegmentsReady.SelectedSnapshot.SnapshotIdentity,
|
||||
RunIdentity: ingestableSnapshot.RunIdentity,
|
||||
SnapshotIdentity: ingestableSnapshot.SelectedSnapshot.SnapshotIdentity,
|
||||
ManifestPath: artifactPaths.ManifestPath,
|
||||
SegmentsPath: artifactPaths.SegmentsPath,
|
||||
CanonicalProjectionPath: artifactPaths.CanonicalProjectionPath,
|
||||
@@ -34,7 +70,7 @@ const toEvent = (
|
||||
|
||||
const payload: UpstreamSnapshotIngested = {
|
||||
RunManifest: runManifest,
|
||||
SegmentRecords: deterministicSegmentsReady.SegmentRecords,
|
||||
SegmentRecords: ingestableSnapshot.SegmentRecords,
|
||||
CanonicalProjectionPath: artifactPaths.CanonicalProjectionPath,
|
||||
SummaryPath: artifactPaths.SummaryPath,
|
||||
}
|
||||
@@ -46,8 +82,8 @@ export const decide = (
|
||||
state: State,
|
||||
command: IngestUpstreamSnapshot,
|
||||
) =>
|
||||
Either.flatMap(validateSnapshotSelection(state, command), (snapshotReady) =>
|
||||
Either.map(decideSegmentRecords(snapshotReady), toEvent),
|
||||
Either.flatMap(selectTrustedSnapshot(state, command), (trustedSnapshot) =>
|
||||
Either.map(decideIngestableSnapshot(trustedSnapshot), emitSnapshotIngested),
|
||||
)
|
||||
|
||||
export const apply = (_state: State, event: Event): State => {
|
||||
@@ -57,10 +93,10 @@ export const apply = (_state: State, event: Event): State => {
|
||||
}
|
||||
}
|
||||
|
||||
export const makeAwaitingSnapshotSelection = (
|
||||
overrides: Partial<AwaitingSnapshotSelection> = {},
|
||||
): AwaitingSnapshotSelection => ({
|
||||
_tag: "AwaitingSnapshotSelection",
|
||||
export const makeAwaitingTrustedBundle = (
|
||||
overrides: Partial<AwaitingTrustedBundle> = {},
|
||||
): AwaitingTrustedBundle => ({
|
||||
_tag: "AwaitingTrustedBundle",
|
||||
RunIdentityRulesDescription: "derive from snapshot identity",
|
||||
BoundaryRulesDescription: "require at least one deterministic boundary proof",
|
||||
RequiredArtifacts: [
|
||||
@@ -73,4 +109,4 @@ export const makeAwaitingSnapshotSelection = (
|
||||
...overrides,
|
||||
})
|
||||
|
||||
export { decideSegmentRecords, validatePreviousRunManifest, validateSnapshotSelection }
|
||||
export { decideIngestableSnapshot }
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Either } from "effect"
|
||||
|
||||
import {
|
||||
type DeterministicSegmentsReady,
|
||||
type Error,
|
||||
type SnapshotReady,
|
||||
applyRunIdentityRules,
|
||||
validateBoundaryProofs,
|
||||
validateRequiredArtifacts,
|
||||
validateSegmentRecords,
|
||||
} from "../index.js"
|
||||
|
||||
export const decideSegmentRecords = (
|
||||
snapshotReady: SnapshotReady,
|
||||
): Either.Either<DeterministicSegmentsReady, Error> =>
|
||||
Either.flatMap(applyRunIdentityRules(snapshotReady.SelectedSnapshot), (derivedRunIdentity) =>
|
||||
Either.flatMap(validateSegmentRecords(snapshotReady.SelectedSnapshot), (segmentRecords) =>
|
||||
Either.flatMap(
|
||||
validateBoundaryProofs(
|
||||
snapshotReady.SelectedSnapshot.SnapshotIdentity,
|
||||
segmentRecords,
|
||||
),
|
||||
(boundaryProofs) =>
|
||||
Either.map(
|
||||
validateRequiredArtifacts(
|
||||
snapshotReady.SelectedSnapshot.SnapshotIdentity,
|
||||
snapshotReady.RequiredArtifacts,
|
||||
segmentRecords,
|
||||
),
|
||||
() => ({
|
||||
_tag: "DeterministicSegmentsReady" as const,
|
||||
RunIdentity: derivedRunIdentity.value,
|
||||
SelectedSnapshot: snapshotReady.SelectedSnapshot,
|
||||
PreviousRunManifest: snapshotReady.PreviousRunManifest,
|
||||
SegmentRecords: segmentRecords,
|
||||
BoundaryProofs: boundaryProofs,
|
||||
RequiredArtifacts: snapshotReady.RequiredArtifacts,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Either } from "effect"
|
||||
|
||||
import {
|
||||
type Error,
|
||||
type IngestUpstreamSnapshot,
|
||||
type RunManifest,
|
||||
type SnapshotReady,
|
||||
type State,
|
||||
foldFailure,
|
||||
makeVerifiedPreviousRunManifest,
|
||||
parseBundleLocation,
|
||||
} from "../index.js"
|
||||
|
||||
export const validatePreviousRunManifest = (
|
||||
manifest: RunManifest,
|
||||
): Either.Either<ReturnType<typeof makeVerifiedPreviousRunManifest>, Error> =>
|
||||
manifest.ManifestPath && manifest.SegmentsPath && manifest.CanonicalProjectionPath
|
||||
? Either.right(makeVerifiedPreviousRunManifest(manifest))
|
||||
: Either.left(
|
||||
foldFailure(manifest.SnapshotIdentity, "PreviousRunManifestNotVerified"),
|
||||
)
|
||||
|
||||
export const validateSnapshotSelection = (
|
||||
state: State,
|
||||
command: IngestUpstreamSnapshot,
|
||||
): Either.Either<SnapshotReady, Error> => {
|
||||
if (state._tag !== "AwaitingSnapshotSelection") {
|
||||
return Either.left(
|
||||
foldFailure(command.SnapshotIdentity, "RunIdentityCouldNotBeDerived"),
|
||||
)
|
||||
}
|
||||
|
||||
return Either.map(
|
||||
parseBundleLocation(command.SnapshotIdentity, command.BundleInput),
|
||||
(bundleLocation) => ({
|
||||
_tag: "SnapshotReady" as const,
|
||||
SelectedSnapshot: {
|
||||
SnapshotIdentity: command.SnapshotIdentity,
|
||||
BundleLocation: bundleLocation,
|
||||
SnapshotMetadata: command.SnapshotMetadata,
|
||||
},
|
||||
PreviousRunManifest: command.PreviousRunManifest,
|
||||
RequiredArtifacts: state.RequiredArtifacts,
|
||||
MaxBundleBytes: state.MaxBundleBytes,
|
||||
ParseBudget: state.ParseBudget,
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import { Effect, Either } from "effect"
|
||||
|
||||
import type {
|
||||
Error,
|
||||
Event,
|
||||
IngestUpstreamSnapshot,
|
||||
State,
|
||||
} from "../models/types.js"
|
||||
import {
|
||||
type Error,
|
||||
type Event,
|
||||
type IngestUpstreamSnapshot,
|
||||
type State,
|
||||
decide,
|
||||
makeAwaitingSnapshotSelection,
|
||||
} from "../index.js"
|
||||
makeAwaitingTrustedBundle,
|
||||
} from "../policies/decideSnapshotIngest.js"
|
||||
|
||||
export const workflow = (
|
||||
command: IngestUpstreamSnapshot,
|
||||
state: State = makeAwaitingSnapshotSelection(),
|
||||
state: State = makeAwaitingTrustedBundle(),
|
||||
): Effect.Effect<Event, Error> =>
|
||||
Effect.gen(function* () {
|
||||
const decision = decide(state, command)
|
||||
|
||||
+13
-16
@@ -3,11 +3,7 @@ import { describe, expect, it } from "vitest"
|
||||
|
||||
import {
|
||||
type IngestUpstreamSnapshot,
|
||||
makeAstNodeKind,
|
||||
makeNormalizedHash,
|
||||
makeRawHash,
|
||||
makeRunIdentity,
|
||||
makeShapeHash,
|
||||
makeSnapshotIdentity,
|
||||
makeTaintedBundleInput,
|
||||
makeTaintedBundleLocation,
|
||||
@@ -20,7 +16,7 @@ import {
|
||||
import {
|
||||
apply,
|
||||
decide,
|
||||
makeAwaitingSnapshotSelection,
|
||||
makeAwaitingTrustedBundle,
|
||||
validatePreviousRunManifest,
|
||||
workflow,
|
||||
} from "../src/contexts/ingest-snapshot/index.js"
|
||||
@@ -42,16 +38,16 @@ describe("ingestSnapshot workflow", () => {
|
||||
const event = await Effect.runPromise(workflow(makeCommand()))
|
||||
|
||||
expect(event._tag).toBe("UpstreamSnapshotIngested")
|
||||
expect(event.payload.RunManifest.RunIdentity).toBe(makeRunIdentity("run:snapshot-001"))
|
||||
expect(event.payload.RunManifest.RunIdentity).toBe("run:snapshot-001")
|
||||
expect(event.payload.RunManifest.ManifestPath).toBe(
|
||||
makeTrustedManifestPath("runs/run:snapshot-001/manifest.json"),
|
||||
"runs/run:snapshot-001/manifest.json",
|
||||
)
|
||||
expect(event.payload.SegmentRecords).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("hard-stops when the bundle location is not parseable", () => {
|
||||
const result = decide(
|
||||
makeAwaitingSnapshotSelection(),
|
||||
makeAwaitingTrustedBundle(),
|
||||
makeCommand({
|
||||
BundleInput: makeTaintedBundleInput(makeTaintedBundleLocation("not-a-path")),
|
||||
}),
|
||||
@@ -64,17 +60,17 @@ describe("ingestSnapshot workflow", () => {
|
||||
})
|
||||
|
||||
it("applies the ingested event into SnapshotIngested state", () => {
|
||||
const result = decide(makeAwaitingSnapshotSelection(), makeCommand())
|
||||
const result = decide(makeAwaitingTrustedBundle(), makeCommand())
|
||||
expect(Either.isRight(result)).toBe(true)
|
||||
if (Either.isRight(result)) {
|
||||
const nextState = apply(makeAwaitingSnapshotSelection(), result.right)
|
||||
const nextState = apply(makeAwaitingTrustedBundle(), result.right)
|
||||
expect(nextState._tag).toBe("SnapshotIngested")
|
||||
if (nextState._tag === "SnapshotIngested") {
|
||||
expect(nextState.RunManifest.CanonicalProjectionPath).toBe(
|
||||
makeTrustedCanonicalProjectionPath("runs/run:snapshot-001/canonical.ts"),
|
||||
"runs/run:snapshot-001/canonical.ts",
|
||||
)
|
||||
expect(nextState.SummaryPath).toBe(
|
||||
makeTrustedSummaryPath("runs/run:snapshot-001/summary.json"),
|
||||
"runs/run:snapshot-001/summary.json",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -115,12 +111,13 @@ describe("ingestSnapshot workflow", () => {
|
||||
|
||||
expect(event.payload.SegmentRecords[0]).toMatchObject({
|
||||
SegmentId: "snapshot-001:root",
|
||||
AstNodeKind: makeAstNodeKind("Program"),
|
||||
AstNodeKind: "Program",
|
||||
Hashes: {
|
||||
RawHash: makeRawHash("raw:snapshot-001"),
|
||||
NormalizedHash: makeNormalizedHash("normalized:snapshot-001"),
|
||||
ShapeHash: makeShapeHash("shape:snapshot-001"),
|
||||
RawHash: "raw:snapshot-001",
|
||||
NormalizedHash: "normalized:snapshot-001",
|
||||
ShapeHash: "shape:snapshot-001",
|
||||
},
|
||||
})
|
||||
expect(event.payload.SegmentRecords[0]?.Hashes.RawHash).toBe("raw:snapshot-001")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user