Current section
Files
Jump to
Current section
Files
src/lightspeed/ops/ga_harness.gleam
//// GA parity harness for 1.0.0 release readiness and reproducibility.
import gleam/list
import lightspeed/ops/channel_harness
import lightspeed/ops/generator_harness
import lightspeed/ops/migration_harness
import lightspeed/ops/performance_harness
import lightspeed/ops/quality_harness
import lightspeed/release/policy
/// M20 GA readiness scenarios.
pub type Scenario {
PolicyFreeze
QualityGate
PerformanceGate
ChannelGate
GeneratorGate
MigrationGate
}
/// One scenario outcome.
pub type ScenarioOutcome {
ScenarioOutcome(
scenario: Scenario,
passed: Bool,
deterministic: Bool,
signature: String,
)
}
/// Full GA report.
pub type Report {
Report(
outcomes: List(ScenarioOutcome),
failed_scenarios: Int,
nondeterministic_failures: Int,
)
}
/// Run all M20 GA readiness scenarios.
pub fn run_matrix() -> Report {
let scenarios = [
PolicyFreeze,
QualityGate,
PerformanceGate,
ChannelGate,
GeneratorGate,
MigrationGate,
]
let outcomes = list.map(scenarios, run_scenario)
Report(
outcomes: outcomes,
failed_scenarios: count_failed(outcomes),
nondeterministic_failures: count_nondeterministic(outcomes),
)
}
/// Run one scenario twice and require stable signature parity.
pub fn run_scenario(scenario: Scenario) -> ScenarioOutcome {
let #(first_passed, first_signature) = evaluate(scenario)
let #(second_passed, second_signature) = evaluate(scenario)
let deterministic =
first_passed == second_passed && first_signature == second_signature
let passed = first_passed && second_passed && deterministic
ScenarioOutcome(
scenario: scenario,
passed: passed,
deterministic: deterministic,
signature: first_signature,
)
}
/// Scenario label.
pub fn scenario_label(scenario: Scenario) -> String {
case scenario {
PolicyFreeze -> "policy_freeze"
QualityGate -> "quality_gate"
PerformanceGate -> "performance_gate"
ChannelGate -> "channel_gate"
GeneratorGate -> "generator_gate"
MigrationGate -> "migration_gate"
}
}
/// Stable pass/fail label.
pub fn pass_fail_label(outcome: ScenarioOutcome) -> String {
case outcome.passed {
True -> "pass"
False -> "fail"
}
}
/// Scenario signature.
pub fn signature(outcome: ScenarioOutcome) -> String {
outcome.signature
}
/// Determinism accessor.
pub fn deterministic(outcome: ScenarioOutcome) -> Bool {
outcome.deterministic
}
/// Scenario accessor.
pub fn scenario(outcome: ScenarioOutcome) -> Scenario {
outcome.scenario
}
/// Outcomes accessor.
pub fn outcomes(report: Report) -> List(ScenarioOutcome) {
report.outcomes
}
/// Failed scenario count.
pub fn failed_scenarios(report: Report) -> Int {
report.failed_scenarios
}
/// Nondeterministic scenario count.
pub fn nondeterministic_failures(report: Report) -> Int {
report.nondeterministic_failures
}
/// Stable report signature for M20 fixtures and release checklist evidence.
pub fn report_signature(report: Report) -> String {
let entries =
list.map(report.outcomes, fn(outcome) {
scenario_label(outcome.scenario)
<> "="
<> pass_fail_label(outcome)
<> ":deterministic="
<> bool_label(outcome.deterministic)
<> ":"
<> outcome.signature
})
join_with(";", entries)
}
fn evaluate(scenario: Scenario) -> #(Bool, String) {
case scenario {
PolicyFreeze -> evaluate_policy_freeze()
QualityGate -> evaluate_quality_gate()
PerformanceGate -> evaluate_performance_gate()
ChannelGate -> evaluate_channel_gate()
GeneratorGate -> evaluate_generator_gate()
MigrationGate -> evaluate_migration_gate()
}
}
fn evaluate_policy_freeze() -> #(Bool, String) {
let release_policy = policy.ga_1_0_0()
let matrix = policy.matrix(release_policy)
let passed =
policy.valid(release_policy)
&& has_milestone(matrix, "M13")
&& has_milestone(matrix, "M14")
&& has_milestone(matrix, "M15")
&& has_milestone(matrix, "M16")
&& has_milestone(matrix, "M17")
&& has_milestone(matrix, "M18")
&& has_milestone(matrix, "M19")
#(passed, policy.policy_signature(release_policy))
}
fn evaluate_quality_gate() -> #(Bool, String) {
let report = quality_harness.run_matrix()
let passed =
quality_harness.failed_scenarios(report) == 0
&& quality_harness.nondeterministic_failures(report) == 0
#(passed, quality_harness.report_signature(report))
}
fn evaluate_performance_gate() -> #(Bool, String) {
let report = performance_harness.run_suite()
let budget =
performance_harness.evaluate_budget(
report,
performance_harness.default_budget(),
)
let passed = performance_harness.budget_failures(budget) == 0
#(passed, performance_harness.report_signature(report))
}
fn evaluate_channel_gate() -> #(Bool, String) {
let first = channel_harness.run_default()
let second = channel_harness.run_default()
let first_signature = channel_harness.report_signature(first)
let second_signature = channel_harness.report_signature(second)
let passed = first_signature == second_signature
#(passed, first_signature)
}
fn evaluate_generator_gate() -> #(Bool, String) {
let first = generator_harness.run_matrix()
let second = generator_harness.run_matrix()
let first_signature = generator_harness.report_signature(first)
let second_signature = generator_harness.report_signature(second)
let passed =
generator_harness.failed_scenarios(first) == 0
&& first_signature == second_signature
#(passed, first_signature)
}
fn evaluate_migration_gate() -> #(Bool, String) {
let first = migration_harness.run_matrix()
let second = migration_harness.run_matrix()
let first_signature = migration_harness.report_signature(first)
let second_signature = migration_harness.report_signature(second)
let passed =
migration_harness.failed_scenarios(first) == 0
&& first_signature == second_signature
#(passed, first_signature)
}
fn has_milestone(entries: List(policy.MatrixEntry), milestone: String) -> Bool {
case entries {
[] -> False
[entry, ..rest] ->
case entry.milestone == milestone {
True -> True
False -> has_milestone(rest, milestone)
}
}
}
fn count_failed(outcomes: List(ScenarioOutcome)) -> Int {
case outcomes {
[] -> 0
[outcome, ..rest] ->
case outcome.passed {
True -> count_failed(rest)
False -> 1 + count_failed(rest)
}
}
}
fn count_nondeterministic(outcomes: List(ScenarioOutcome)) -> Int {
case outcomes {
[] -> 0
[outcome, ..rest] ->
case outcome.deterministic {
True -> count_nondeterministic(rest)
False -> 1 + count_nondeterministic(rest)
}
}
}
fn bool_label(value: Bool) -> String {
case value {
True -> "true"
False -> "false"
}
}
fn join_with(separator: String, values: List(String)) -> String {
case values {
[] -> ""
[value] -> value
[value, ..rest] -> value <> separator <> join_with(separator, rest)
}
}