///|
pub(all) enum AcceptanceLevel {
AcceptanceReady
AcceptanceNearlyReady
AcceptanceNeedsWork
AcceptanceBlocked
} derive(Debug, Eq)
///|
pub(all) struct AcceptanceReview {
report : AuditReport
evidence : EvidenceMatrix
quality : QualityMatrix
release_plan : ReleasePlan
level : AcceptanceLevel
decision : String
} derive(Debug, Eq)
///|
pub fn review_acceptance(input : AuditInput) -> AcceptanceReview {
let report = audit_project(input)
let evidence = evidence_from_report(input, report)
let quality = quality_from_report(input, report)
let release_plan = release_plan_from_report(input, report)
let level = acceptance_level(report, evidence, quality, release_plan)
AcceptanceReview::{
report,
evidence,
quality,
release_plan,
level,
decision: acceptance_decision_text(level),
}
}
///|
pub fn AcceptanceReview::is_likely_to_pass(self : AcceptanceReview) -> Bool {
self.level == AcceptanceReady
}
///|
pub fn AcceptanceReview::is_submission_risky(self : AcceptanceReview) -> Bool {
self.level == AcceptanceNeedsWork || self.level == AcceptanceBlocked
}
///|
pub fn AcceptanceReview::level_name(self : AcceptanceReview) -> String {
acceptance_level_name(self.level)
}
///|
pub fn AcceptanceReview::summary(self : AcceptanceReview) -> String {
"acceptance=\{self.level_name()}, readiness=\{self.report.score.readiness}, evidence=\{self.evidence.coverage_percent()}, quality=\{self.quality.normalized_score}"
}
///|
pub fn AcceptanceReview::blocking_reasons(
self : AcceptanceReview,
) -> Array[String] {
let reasons : Array[String] = []
acceptance_collect_blocking_reasons(
reasons,
self.report,
self.evidence,
self.release_plan,
)
reasons
}
///|
pub fn AcceptanceReview::positive_signals(
self : AcceptanceReview,
) -> Array[String] {
let signals : Array[String] = []
if self.report.score.error_count == 0 {
signals.push("standard audit has no errors")
}
if self.report.score.warning_count == 0 {
signals.push("standard audit has no warnings")
}
if self.evidence.coverage_percent() >= 90 {
signals.push("acceptance evidence is almost complete")
}
if self.quality.is_competitive() {
signals.push("quality matrix is competitive")
}
if release_plan_has_command(self.release_plan, "moon publish --dry-run") {
signals.push("release plan includes Mooncakes dry-run validation")
}
signals
}
///|
pub fn AcceptanceReview::next_actions(self : AcceptanceReview) -> Array[String] {
let actions : Array[String] = []
let blockers = self.blocking_reasons()
let mut blocker_index = 0
while blocker_index < blockers.length() {
actions.push(blockers[blocker_index])
blocker_index += 1
}
let improvements = self.quality.improvement_notes()
let mut improve_index = 0
while improve_index < improvements.length() {
actions.push(improvements[improve_index])
improve_index += 1
}
if actions.length() == 0 {
actions.push(
"keep the repository public, tag the release, and preserve CI/test records",
)
}
actions
}
///|
pub fn AcceptanceReview::to_markdown(self : AcceptanceReview) -> String {
let mut out = "# Acceptance Review\n\n"
out = out + "- Decision: " + self.decision + "\n"
out = out + "- Level: " + self.level_name() + "\n"
out = out +
"- Audit readiness: " +
self.report.score.readiness.to_string() +
"/100\n"
out = out +
"- Evidence coverage: " +
self.evidence.coverage_percent().to_string() +
"%\n"
out = out +
"- Quality score: " +
self.quality.normalized_score.to_string() +
"/100\n"
out = out +
"- Release blockers: " +
self.release_plan.blockers.length().to_string() +
"\n\n"
let blockers = self.blocking_reasons()
if blockers.length() > 0 {
out = out + "## Blocking Reasons\n\n"
let mut blocker_index = 0
while blocker_index < blockers.length() {
out = out + "- " + blockers[blocker_index] + "\n"
blocker_index += 1
}
out = out + "\n"
}
let signals = self.positive_signals()
if signals.length() > 0 {
out = out + "## Positive Signals\n\n"
let mut signal_index = 0
while signal_index < signals.length() {
out = out + "- " + signals[signal_index] + "\n"
signal_index += 1
}
out = out + "\n"
}
out = out + "## Next Actions\n\n"
let actions = self.next_actions()
let mut action_index = 0
while action_index < actions.length() {
out = out + "- " + actions[action_index] + "\n"
action_index += 1
}
out
}
///|
pub fn acceptance_level_name(level : AcceptanceLevel) -> String {
match level {
AcceptanceReady => "ready"
AcceptanceNearlyReady => "nearly-ready"
AcceptanceNeedsWork => "needs-work"
AcceptanceBlocked => "blocked"
}
}
///|
pub fn acceptance_reviews_table(reviews : Array[AcceptanceReview]) -> String {
let mut out = "| # | Level | Readiness | Evidence | Quality | Decision |\n| --- | --- | --- | --- | --- | --- |\n"
let mut index = 0
while index < reviews.length() {
let review = reviews[index]
out = out + "| " + (index + 1).to_string()
out = out + " | " + review.level_name()
out = out + " | " + review.report.score.readiness.to_string()
out = out + " | " + review.evidence.coverage_percent().to_string() + "%"
out = out + " | " + review.quality.normalized_score.to_string()
out = out + " | " + review.decision + " |\n"
index += 1
}
out
}
///|
fn acceptance_level(
report : AuditReport,
evidence : EvidenceMatrix,
quality : QualityMatrix,
release_plan : ReleasePlan,
) -> AcceptanceLevel {
if report.score.error_count > 0 ||
acceptance_missing_hard_evidence(evidence) ||
release_plan.is_blocked() {
return AcceptanceBlocked
}
if report.score.warning_count == 0 &&
evidence.is_complete() &&
quality.normalized_score >= 85 {
return AcceptanceReady
}
if evidence.coverage_percent() >= 85 && quality.normalized_score >= 75 {
AcceptanceNearlyReady
} else {
AcceptanceNeedsWork
}
}
///|
fn acceptance_decision_text(level : AcceptanceLevel) -> String {
match level {
AcceptanceReady => "likely to pass local acceptance checks"
AcceptanceNearlyReady =>
"close to acceptance, but review the remaining evidence"
AcceptanceNeedsWork =>
"not ready yet; improve documentation, CI or quality signals"
AcceptanceBlocked => "blocked by one or more hard acceptance requirements"
}
}
///|
fn acceptance_missing_hard_evidence(evidence : EvidenceMatrix) -> Bool {
!evidence.has("moonbit-project") ||
!evidence.has("package-name") ||
!evidence.has("version") ||
!evidence.has("readme-present") ||
!evidence.has("ci-check") ||
!evidence.has("ci-build") ||
!evidence.has("ci-test") ||
!evidence.has("license") ||
!evidence.has("public-repo") ||
!evidence.has("mooncakes")
}
///|
fn acceptance_collect_blocking_reasons(
reasons : Array[String],
report : AuditReport,
evidence : EvidenceMatrix,
release_plan : ReleasePlan,
) -> Unit {
if report.score.error_count > 0 {
reasons.push("fix audit errors before acceptance")
}
acceptance_add_missing_evidence(
reasons, evidence, "moonbit-project", "MoonBit project metadata must be valid",
)
acceptance_add_missing_evidence(
reasons, evidence, "package-name", "Mooncakes package namespace must be valid",
)
acceptance_add_missing_evidence(
reasons, evidence, "version", "moon.mod version must be semantic",
)
acceptance_add_missing_evidence(
reasons, evidence, "readme-present", "README must exist",
)
acceptance_add_missing_evidence(
reasons, evidence, "ci-check", "CI must run moon check",
)
acceptance_add_missing_evidence(
reasons, evidence, "ci-build", "CI must run moon build",
)
acceptance_add_missing_evidence(
reasons, evidence, "ci-test", "CI must run moon test",
)
acceptance_add_missing_evidence(
reasons, evidence, "license", "LICENSE must be complete and OSI-approved",
)
acceptance_add_missing_evidence(
reasons, evidence, "public-repo", "GitHub repository must be public",
)
acceptance_add_missing_evidence(
reasons, evidence, "mooncakes", "package must be published to mooncakes.io",
)
let mut index = 0
while index < release_plan.blockers.length() {
acceptance_push_unique(reasons, release_plan.blockers[index])
index += 1
}
}
///|
fn acceptance_add_missing_evidence(
reasons : Array[String],
evidence : EvidenceMatrix,
code : String,
message : String,
) -> Unit {
if !evidence.has(code) {
acceptance_push_unique(reasons, message)
}
}
///|
fn acceptance_push_unique(items : Array[String], value : String) -> Unit {
let mut index = 0
while index < items.length() {
if items[index] == value {
return
}
index += 1
}
items.push(value)
}