///|
pub(all) struct AcceptanceProfile {
effective_moonbit_loc : Int
target_moonbit_loc : Int
ci_status : String
mooncakes_build_status : String
latest_commit_author : String
latest_commit_email : String
contest_commit_count : Int
published_version : String
} derive(Eq, Debug)
///|
pub fn AcceptanceProfile::AcceptanceProfile(
effective_moonbit_loc? : Int = 0,
target_moonbit_loc? : Int = 4000,
ci_status? : String = "",
mooncakes_build_status? : String = "",
latest_commit_author? : String = "",
latest_commit_email? : String = "",
contest_commit_count? : Int = 0,
published_version? : String = "",
) -> AcceptanceProfile {
{
effective_moonbit_loc,
target_moonbit_loc,
ci_status,
mooncakes_build_status,
latest_commit_author,
latest_commit_email,
contest_commit_count,
published_version,
}
}
///|
pub(all) struct AcceptanceSignal {
id : String
severity : Severity
title : String
evidence : String
action : String
hard_blocker : Bool
} derive(Eq, Debug)
///|
pub(all) struct AcceptanceReview {
audit : AuditReport
identity : IdentityReport
doc_proof : DocProofReport
profile : AcceptanceProfile
signals : Array[AcceptanceSignal]
score : Int
verdict : String
} derive(Eq, Debug)
///|
pub fn final_acceptance_review(
snapshot : ProjectSnapshot,
canonical : SubmissionIdentity,
submission_text : String,
markdown : String,
sources : Array[SourceEvidence],
profile? : AcceptanceProfile = AcceptanceProfile::AcceptanceProfile(),
git_user_name? : String = "",
git_user_email? : String = "",
git_remote? : String = "",
) -> AcceptanceReview {
let audit_report = audit(snapshot)
let identity_report = acceptance_identity_report(
canonical,
snapshot,
submission_text,
git_user_name~,
git_user_email~,
git_remote~,
)
let proof = doc_proof(markdown, sources)
let signals = acceptance_signals(
audit_report, identity_report, proof, profile,
)
let score = acceptance_score(
audit_report, identity_report, proof, profile, signals,
)
{
audit: audit_report,
identity: identity_report,
doc_proof: proof,
profile,
signals,
score,
verdict: acceptance_verdict(signals, score),
}
}
///|
pub fn final_acceptance_markdown(
snapshot : ProjectSnapshot,
canonical : SubmissionIdentity,
submission_text : String,
markdown : String,
sources : Array[SourceEvidence],
profile? : AcceptanceProfile = AcceptanceProfile::AcceptanceProfile(),
git_user_name? : String = "",
git_user_email? : String = "",
git_remote? : String = "",
) -> String {
final_acceptance_review(
snapshot,
canonical,
submission_text,
markdown,
sources,
profile~,
git_user_name~,
git_user_email~,
git_remote~,
).to_markdown()
}
///|
pub fn final_acceptance_json(
snapshot : ProjectSnapshot,
canonical : SubmissionIdentity,
submission_text : String,
markdown : String,
sources : Array[SourceEvidence],
profile? : AcceptanceProfile = AcceptanceProfile::AcceptanceProfile(),
git_user_name? : String = "",
git_user_email? : String = "",
git_remote? : String = "",
) -> String {
final_acceptance_review(
snapshot,
canonical,
submission_text,
markdown,
sources,
profile~,
git_user_name~,
git_user_email~,
git_remote~,
).to_json()
}
///|
pub fn AcceptanceReview::has_hard_blockers(self : AcceptanceReview) -> Bool {
for signal in self.signals {
if signal.hard_blocker {
return true
}
}
false
}
///|
pub fn AcceptanceReview::hard_blocker_count(self : AcceptanceReview) -> Int {
let mut total = 0
for signal in self.signals {
if signal.hard_blocker {
total += 1
}
}
total
}
///|
pub fn AcceptanceReview::warning_count(self : AcceptanceReview) -> Int {
let mut total = 0
for signal in self.signals {
if signal.severity == Warn {
total += 1
}
}
total
}
///|
pub fn AcceptanceReview::line_gap(self : AcceptanceReview) -> Int {
loc_gap(self.profile)
}
///|
pub fn AcceptanceReview::to_markdown(self : AcceptanceReview) -> String {
let out = StringBuilder()
out.write_string("# HarborCheck Final Acceptance Review\n\n")
out.write_string("- Verdict: " + self.verdict + "\n")
out.write_string("- Score: " + self.score.to_string() + "/100\n")
out.write_string("- Audit verdict: " + self.audit.verdict + "\n")
out.write_string("- Identity verdict: " + self.identity.verdict + "\n")
out.write_string("- Doc proof verdict: " + self.doc_proof.verdict + "\n")
out.write_string(
"- Effective MoonBit LOC: " +
self.profile.effective_moonbit_loc.to_string() +
"/" +
self.profile.target_moonbit_loc.to_string() +
"\n",
)
out.write_string(
"- Hard blockers: " + self.hard_blocker_count().to_string() + "\n",
)
out.write_string("- Warnings: " + self.warning_count().to_string() + "\n\n")
out.write_string("## Signals\n\n")
for signal in self.signals {
out.write_string("- [" + severity_text(signal.severity) + "] ")
out.write_string(signal.id + ": " + signal.title)
if signal.evidence != "" {
out.write_string(" -- " + signal.evidence)
}
if signal.severity != Pass && signal.action != "" {
out.write_string(" Action: " + signal.action)
}
out.write_string("\n")
}
out.write_string("\n## Next Actions\n\n")
let actions = self.next_actions()
if actions.length() == 0 {
out.write_string("- Keep CI green and submit the current public links.\n")
} else {
for action in actions {
out.write_string("- " + action + "\n")
}
}
out.to_string()
}
///|
pub fn AcceptanceReview::to_json(self : AcceptanceReview) -> String {
let out = StringBuilder()
out.write_string("{")
out.write_string("\"verdict\":\"" + json_escape(self.verdict) + "\",")
out.write_string("\"score\":" + self.score.to_string() + ",")
out.write_string(
"\"hard_blockers\":" + self.hard_blocker_count().to_string() + ",",
)
out.write_string("\"warnings\":" + self.warning_count().to_string() + ",")
out.write_string(
"\"effective_moonbit_loc\":" +
self.profile.effective_moonbit_loc.to_string() +
",",
)
out.write_string(
"\"target_moonbit_loc\":" +
self.profile.target_moonbit_loc.to_string() +
",",
)
out.write_string("\"audit\":\"" + json_escape(self.audit.verdict) + "\",")
out.write_string(
"\"identity\":\"" + json_escape(self.identity.verdict) + "\",",
)
out.write_string(
"\"doc_proof\":\"" + json_escape(self.doc_proof.verdict) + "\",",
)
out.write_string("\"signals\":[")
for i in 0.. 0 {
out.write_string(",")
}
out.write_string("{")
out.write_string("\"id\":\"" + json_escape(signal.id) + "\",")
out.write_string("\"severity\":\"" + severity_text(signal.severity) + "\",")
out.write_string("\"title\":\"" + json_escape(signal.title) + "\",")
out.write_string("\"evidence\":\"" + json_escape(signal.evidence) + "\",")
out.write_string("\"hard_blocker\":" + signal.hard_blocker.to_string())
out.write_string("}")
}
out.write_string("]}")
out.to_string()
}
///|
pub fn AcceptanceReview::next_actions(self : AcceptanceReview) -> Array[String] {
let actions = Array::new(capacity=self.signals.length())
for signal in self.signals {
if signal.severity != Pass && signal.action != "" {
actions.push(signal.action)
}
}
actions
}
///|
fn acceptance_signals(
audit_report : AuditReport,
identity_report : IdentityReport,
proof : DocProofReport,
profile : AcceptanceProfile,
) -> Array[AcceptanceSignal] {
let signals = Array::new(capacity=16)
push_audit_signal(signals, audit_report)
push_identity_signal(signals, identity_report)
push_doc_signal(signals, proof)
push_loc_signal(signals, profile)
push_ci_status_signal(signals, profile)
push_mooncakes_status_signal(signals, profile)
push_commit_signal(signals, profile)
push_version_signal(signals, profile)
signals
}
///|
fn push_audit_signal(
signals : Array[AcceptanceSignal],
report : AuditReport,
) -> Unit {
if report.metrics.fail_count == 0 {
signals.push(
acceptance_pass(
"audit.ready",
"Repository checklist has no blocking failures",
report.score.to_string() + "/100",
),
)
} else {
signals.push(
acceptance_fail(
"audit.blockers",
"Repository checklist has blocking failures",
report.metrics.fail_count.to_string() + " fail findings",
"Fix the audit report before submitting final acceptance evidence.",
true,
),
)
}
}
///|
fn push_identity_signal(
signals : Array[AcceptanceSignal],
report : IdentityReport,
) -> Unit {
if report.verdict == "consistent" {
signals.push(
acceptance_pass(
"identity.consistent",
"Applicant, GitHub and Mooncakes identities are consistent",
report.matching_count().to_string() + " matching evidence items",
),
)
} else {
signals.push(
acceptance_fail(
"identity.mismatch",
"Identity evidence is inconsistent",
report.mismatch_count().to_string() + " mismatches",
"Keep applicant name, email, GitHub owner, git author and Mooncakes namespace aligned.",
true,
),
)
}
}
///|
fn push_doc_signal(
signals : Array[AcceptanceSignal],
proof : DocProofReport,
) -> Unit {
if proof.verdict == "ready" {
signals.push(
acceptance_pass(
"doc-proof.ready",
"README examples and source records are reviewable",
proof.runnable_snippet_count.to_string() + " runnable snippets",
),
)
} else {
signals.push(
acceptance_fail(
"doc-proof.needs-work",
"README examples or source records need work",
proof.verdict,
"Add runnable MoonBit snippets and clear source/license evidence.",
true,
),
)
}
}
///|
fn push_loc_signal(
signals : Array[AcceptanceSignal],
profile : AcceptanceProfile,
) -> Unit {
let gap = loc_gap(profile)
if profile.effective_moonbit_loc <= 0 {
signals.push(
acceptance_warn(
"scale.unknown", "Effective MoonBit LOC was not provided", "unknown", "Count nonblank non-comment .mbt lines before final submission.",
),
)
} else if gap <= 0 {
signals.push(
acceptance_pass(
"scale.target-met",
"Effective MoonBit LOC reaches the reference target",
profile.effective_moonbit_loc.to_string() +
"/" +
profile.target_moonbit_loc.to_string(),
),
)
} else {
signals.push(
acceptance_warn(
"scale.below-reference",
"Effective MoonBit LOC is below the reference scale",
profile.effective_moonbit_loc.to_string() +
"/" +
profile.target_moonbit_loc.to_string() +
" gap=" +
gap.to_string(),
"Prefer adding real reviewer-facing capability instead of padding code.",
),
)
}
}
///|
fn push_ci_status_signal(
signals : Array[AcceptanceSignal],
profile : AcceptanceProfile,
) -> Unit {
let status = lower(profile.ci_status)
if status == "success" || status == "passed" {
signals.push(
acceptance_pass(
"ci.remote-success",
"Remote CI is successful",
profile.ci_status,
),
)
} else if status == "" {
signals.push(
acceptance_warn(
"ci.status-unknown", "Remote CI status was not provided", "unknown", "Open GitHub Actions and confirm the latest main-branch run is successful.",
),
)
} else {
signals.push(
acceptance_fail(
"ci.remote-failed",
"Remote CI is not successful",
profile.ci_status,
"Fix GitHub Actions before final acceptance.",
true,
),
)
}
}
///|
fn push_mooncakes_status_signal(
signals : Array[AcceptanceSignal],
profile : AcceptanceProfile,
) -> Unit {
let status = lower(profile.mooncakes_build_status)
if status == "success" || status == "passed" {
signals.push(
acceptance_pass(
"mooncakes.build-success",
"Mooncakes build status is successful",
profile.mooncakes_build_status,
),
)
} else if status == "" {
signals.push(
acceptance_warn(
"mooncakes.status-unknown", "Mooncakes build status was not provided", "unknown",
"Check the Mooncakes manifest before final acceptance.",
),
)
} else {
signals.push(
acceptance_fail(
"mooncakes.build-failed",
"Mooncakes build status is not successful",
profile.mooncakes_build_status,
"Republish or fix package docs until Mooncakes reports success.",
true,
),
)
}
}
///|
fn push_commit_signal(
signals : Array[AcceptanceSignal],
profile : AcceptanceProfile,
) -> Unit {
if profile.contest_commit_count >= 5 {
signals.push(
acceptance_pass(
"git.contest-history",
"Contest-period commit history is traceable",
profile.contest_commit_count.to_string() + " commits",
),
)
} else {
signals.push(
acceptance_warn(
"git.short-history",
"Contest-period commit history is short",
profile.contest_commit_count.to_string() + " commits",
"Keep meaningful implementation, test, documentation and release commits.",
),
)
}
if profile.latest_commit_author != "" || profile.latest_commit_email != "" {
signals.push(
acceptance_pass(
"git.latest-author",
"Latest commit author evidence is recorded",
profile.latest_commit_author + " <" + profile.latest_commit_email + ">",
),
)
} else {
signals.push(
acceptance_warn(
"git.latest-author-unknown", "Latest commit author evidence was not provided",
"unknown", "Record the latest commit author when preparing final acceptance notes.",
),
)
}
}
///|
fn push_version_signal(
signals : Array[AcceptanceSignal],
profile : AcceptanceProfile,
) -> Unit {
if semver_like(profile.published_version) {
signals.push(
acceptance_pass(
"release.version",
"Published version is SemVer-like",
profile.published_version,
),
)
} else if profile.published_version == "" {
signals.push(
acceptance_warn(
"release.version-unknown", "Published version was not provided", "unknown",
"Record the published Mooncakes version in final notes.",
),
)
} else {
signals.push(
acceptance_warn(
"release.version-format",
"Published version does not look like x.y.z",
profile.published_version,
"Use SemVer-style versions for Mooncakes releases.",
),
)
}
}
///|
fn acceptance_pass(
id : String,
title : String,
evidence : String,
) -> AcceptanceSignal {
{ id, severity: Pass, title, evidence, action: "", hard_blocker: false }
}
///|
fn acceptance_warn(
id : String,
title : String,
evidence : String,
action : String,
) -> AcceptanceSignal {
{ id, severity: Warn, title, evidence, action, hard_blocker: false }
}
///|
fn acceptance_fail(
id : String,
title : String,
evidence : String,
action : String,
hard_blocker : Bool,
) -> AcceptanceSignal {
{ id, severity: Fail, title, evidence, action, hard_blocker }
}
///|
fn loc_gap(profile : AcceptanceProfile) -> Int {
if profile.target_moonbit_loc <= profile.effective_moonbit_loc {
0
} else {
profile.target_moonbit_loc - profile.effective_moonbit_loc
}
}
///|
fn acceptance_score(
audit_report : AuditReport,
identity_report : IdentityReport,
proof : DocProofReport,
profile : AcceptanceProfile,
signals : Array[AcceptanceSignal],
) -> Int {
let mut score = 100
score -= (100 - audit_report.score) / 2
score -= (100 - identity_report.score) / 2
if proof.verdict != "ready" {
score -= 18
}
if loc_gap(profile) > 0 && profile.effective_moonbit_loc > 0 {
score -= 4
}
for signal in signals {
match signal.severity {
Pass => ()
Warn => score -= 2
Fail => score -= 12
}
}
score.clamp(min=0, max=100)
}
///|
fn acceptance_verdict(signals : Array[AcceptanceSignal], score : Int) -> String {
let mut blockers = 0
let mut warnings = 0
for signal in signals {
if signal.hard_blocker {
blockers += 1
} else if signal.severity == Warn {
warnings += 1
}
}
if blockers > 0 {
"blocked"
} else if score >= 90 && warnings == 0 {
"acceptance-ready"
} else if score >= 80 {
"review-ready-with-notes"
} else {
"needs-polish"
}
}