Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/app-builder/generation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * as GenerationKernel from "./kernel.js"
export { composeCatalog } from "./catalog.js"
export * as Intent from "./intent.js"
export * as Planner from "./planner.js"
export * as Pr130Regeneration from "./regeneration.js"
export * as Replay from "./replay.js"
export * as TodoEvolution from "./evolution.js"
export * as TodoGeneration from "./generators/index.js"
Expand Down
89 changes: 89 additions & 0 deletions packages/app-builder/generation/src/regeneration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
import * as Schema from "effect/Schema"

export const Pr130EvidenceClass = Schema.Literals([
"unit-1-replay-completeness",
"unit-2-typed-kernel-catalog",
"unit-3-generation-surfaces",
"unit-4-atomic-todo-materialization",
"unit-5-todo-v1-parity",
"unit-6-public-cli-lifecycle",
])
export type Pr130EvidenceClass = typeof Pr130EvidenceClass.Type
const CanonicalEvidenceClasses = Object.freeze(Pr130EvidenceClass.literals)

export const Pr130FrozenHead = "d5928f99a122b04cb70ff68bfa02fa2d5648a5fc"

export const Pr130ExpectedEvidenceIdentities: Readonly<Record<Pr130EvidenceClass, string>> = Object.freeze({
"unit-1-replay-completeness": "613a9654a183910956cc72153f7f8c33b3a78282",
"unit-2-typed-kernel-catalog": "b85aabf05d6cb800e017cd5f4dc4e2ccc6a03e7f",
"unit-3-generation-surfaces": "76aab96389f0f0c7c4ab02b20dbf53b7e60abcfa",
"unit-4-atomic-todo-materialization": "21b92cc564ac806a82e50640552fdf8f0d6d674a",
"unit-5-todo-v1-parity": "2ad43e0cca44a0ed34db27fa29b344a71fc8e214",
"unit-6-public-cli-lifecycle": "54b9677b541cdb4fb3f0be11d5b4e77914f8a4c7",
})

export const Pr130RegenerationProof = Schema.Struct({
version: Schema.Literal("effectify.app-builder-pr130-regeneration-proof/1"),
evidence: Schema.Array(
Schema.Struct({
evidenceClass: Pr130EvidenceClass,
identity: Schema.String,
}),
),
pr130Head: Schema.String,
})
export type Pr130RegenerationProof = typeof Pr130RegenerationProof.Type

export const Pr130RegenerationBlockReason = Schema.TaggedUnion({
MissingEvidence: { evidenceClass: Pr130EvidenceClass },
MismatchedEvidence: {
actualIdentities: Schema.Array(Schema.String),
evidenceClass: Pr130EvidenceClass,
expectedIdentity: Schema.String,
},
WrongPr130Head: { actualHead: Schema.String, expectedHead: Schema.String },
})
export type Pr130RegenerationBlockReason = typeof Pr130RegenerationBlockReason.Type

export class Pr130RegenerationBlocked extends Data.TaggedError("Pr130RegenerationBlocked")<{
readonly reasons: ReadonlyArray<Pr130RegenerationBlockReason>
}> {}

export interface Pr130RegenerationEligibility {
readonly prNumber: 130
readonly status: "eligible"
}

const eligibility: Pr130RegenerationEligibility = Object.freeze({ prNumber: 130, status: "eligible" })

/** Evaluates canonical evidence only; it owns no external read or mutation capability. */
export const requirePr130RegenerationEligibility = (
proof: Pr130RegenerationProof,
): Effect.Effect<Pr130RegenerationEligibility, Pr130RegenerationBlocked> => {
const reasons: Array<Pr130RegenerationBlockReason> = []
for (const evidenceClass of CanonicalEvidenceClasses) {
const expectedIdentity = Pr130ExpectedEvidenceIdentities[evidenceClass]
const actualIdentities = proof.evidence
.filter((evidence) => evidence.evidenceClass === evidenceClass)
.map((evidence) => evidence.identity)
.sort()
if (actualIdentities.length === 0) {
reasons.push({ _tag: "MissingEvidence", evidenceClass })
} else if (actualIdentities.length !== 1 || actualIdentities[0] !== expectedIdentity) {
reasons.push({
_tag: "MismatchedEvidence",
actualIdentities: Object.freeze(actualIdentities),
evidenceClass,
expectedIdentity,
})
}
}
if (proof.pr130Head !== Pr130FrozenHead) {
reasons.push({ _tag: "WrongPr130Head", actualHead: proof.pr130Head, expectedHead: Pr130FrozenHead })
}
return reasons.length === 0
? Effect.succeed(eligibility)
: Effect.fail(new Pr130RegenerationBlocked({ reasons: Object.freeze(reasons) }))
}
161 changes: 161 additions & 0 deletions packages/app-builder/generation/tests/regeneration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { readFileSync } from "node:fs"
import { fileURLToPath } from "node:url"
import { expect, it } from "@effect/vitest"
import * as Effect from "effect/Effect"
import * as Result from "effect/Result"
import * as Generation from "../src/index.js"
import {
Pr130EvidenceClass,
Pr130FrozenHead,
type Pr130RegenerationProof,
requirePr130RegenerationEligibility,
} from "../src/regeneration.js"

const ExpectedCanonicalEvidence: Pr130RegenerationProof["evidence"] = Object.freeze([
{
evidenceClass: "unit-1-replay-completeness",
identity: "613a9654a183910956cc72153f7f8c33b3a78282",
},
{
evidenceClass: "unit-2-typed-kernel-catalog",
identity: "b85aabf05d6cb800e017cd5f4dc4e2ccc6a03e7f",
},
{
evidenceClass: "unit-3-generation-surfaces",
identity: "76aab96389f0f0c7c4ab02b20dbf53b7e60abcfa",
},
{
evidenceClass: "unit-4-atomic-todo-materialization",
identity: "21b92cc564ac806a82e50640552fdf8f0d6d674a",
},
{
evidenceClass: "unit-5-todo-v1-parity",
identity: "2ad43e0cca44a0ed34db27fa29b344a71fc8e214",
},
{
evidenceClass: "unit-6-public-cli-lifecycle",
identity: "54b9677b541cdb4fb3f0be11d5b4e77914f8a4c7",
},
])
const ExpectedCanonicalClasses = Object.freeze(ExpectedCanonicalEvidence.map(({ evidenceClass }) => evidenceClass))
const ExpectedPr130Head = "d5928f99a122b04cb70ff68bfa02fa2d5648a5fc"

const completeProof = (): Pr130RegenerationProof => ({
version: "effectify.app-builder-pr130-regeneration-proof/1",
evidence: ExpectedCanonicalEvidence.map((evidence) => ({ ...evidence })),
pr130Head: ExpectedPr130Head,
})

it.effect("incomplete canonical proof blocks PR #130 regeneration before effects", () =>
Effect.gen(function* () {
const failure = yield* requirePr130RegenerationEligibility({
...completeProof(),
evidence: [],
}).pipe(Effect.flip)

expect(failure).toMatchObject({ _tag: "Pr130RegenerationBlocked" })
expect(failure.reasons).toEqual(
ExpectedCanonicalClasses.map((evidenceClass) => ({ _tag: "MissingEvidence", evidenceClass })),
)
}),
)

it.effect("each canonical Unit evidence class is independently required", () =>
Effect.gen(function* () {
for (const missing of ExpectedCanonicalClasses) {
const proof = completeProof()
const failure = yield* requirePr130RegenerationEligibility({
...proof,
evidence: proof.evidence.filter(({ evidenceClass }) => evidenceClass !== missing),
}).pipe(Effect.flip)

expect(failure.reasons).toEqual([{ _tag: "MissingEvidence", evidenceClass: missing }])
}
}),
)

it.effect("mismatched evidence identities block PR #130 regeneration deterministically", () =>
Effect.gen(function* () {
const evidence = completeProof()
.evidence.map(({ evidenceClass }) => ({ evidenceClass, identity: `stale:${evidenceClass}` }))
.reverse()
const failure = yield* requirePr130RegenerationEligibility({ ...completeProof(), evidence }).pipe(Effect.flip)

expect(failure.reasons).toEqual(
ExpectedCanonicalEvidence.map(({ evidenceClass, identity }) => ({
_tag: "MismatchedEvidence",
actualIdentities: [`stale:${evidenceClass}`],
evidenceClass,
expectedIdentity: identity,
})),
)
}),
)

it.effect("wrong frozen PR #130 head blocks eligibility", () =>
Effect.gen(function* () {
const failure = yield* requirePr130RegenerationEligibility({
...completeProof(),
pr130Head: "54b9677b541cdb4fb3f0be11d5b4e77914f8a4c7",
}).pipe(Effect.flip)

expect(failure.reasons).toEqual([
{
_tag: "WrongPr130Head",
actualHead: "54b9677b541cdb4fb3f0be11d5b4e77914f8a4c7",
expectedHead: Pr130FrozenHead,
},
])
}),
)

it.effect("complete canonical proof returns regeneration eligibility only", () =>
Effect.gen(function* () {
const proof = Object.freeze({
...completeProof(),
evidence: Object.freeze(completeProof().evidence.map((entry) => Object.freeze(entry))),
})
const before = structuredClone(proof)
const eligibility = yield* requirePr130RegenerationEligibility(proof)

expect(eligibility).toEqual({ prNumber: 130, status: "eligible" })
expect(proof).toEqual(before)
}),
)

it("frozen PR #130 head remains typed input evidence only", () => {
const source = readFileSync(fileURLToPath(new URL("../src/regeneration.ts", import.meta.url)), "utf8")

expect(Pr130FrozenHead).toBe("d5928f99a122b04cb70ff68bfa02fa2d5648a5fc")
expect(source).not.toMatch(/node:|github|octokit|\bfetch\b|Effect\.(promise|tryPromise)/i)
})

it.effect("public evidence metadata cannot weaken the canonical Unit 6 requirement", () =>
Effect.gen(function* () {
const publicClasses = Pr130EvidenceClass.literals
const original = [...publicClasses]
const deleted = Reflect.deleteProperty(publicClasses, "5")
const shortened = Reflect.set(publicClasses, "length", 5)
const outcome = yield* Effect.result(
requirePr130RegenerationEligibility({
...completeProof(),
evidence: ExpectedCanonicalEvidence.filter(
({ evidenceClass }) => evidenceClass !== "unit-6-public-cli-lifecycle",
),
}),
)
for (const [index, evidenceClass] of original.entries()) {
Reflect.set(publicClasses, String(index), evidenceClass)
}
Reflect.set(publicClasses, "length", original.length)

expect(Result.isFailure(outcome)).toBe(true)
if (Result.isFailure(outcome)) {
expect(outcome.failure.reasons).toEqual([
{ _tag: "MissingEvidence", evidenceClass: "unit-6-public-cli-lifecycle" },
])
}
expect("Pr130EvidenceClasses" in Generation.Pr130Regeneration).toBe(false)
expect([deleted, shortened, Object.isFrozen(publicClasses)]).toEqual([false, false, true])
}),
)
Loading