Compare commits

...

3 Commits

10 changed files with 201 additions and 212 deletions
+2 -2
View File
@@ -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
+41 -8
View File
@@ -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,
+60 -17
View File
@@ -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,
): {
+9 -9
View File
@@ -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
View File
@@ -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")
})
})