///|
pub(all) struct DossierEvidence {
id : String
title : String
source : String
proof_path : String
summary : String
required : Bool
complete : Bool
} derive(Eq, Debug)
///|
pub fn DossierEvidence::DossierEvidence(
id? : String = "",
title? : String = "",
source? : String = "",
proof_path? : String = "",
summary? : String = "",
required? : Bool = false,
complete? : Bool = false,
) -> DossierEvidence {
{ id, title, source, proof_path, summary, required, complete }
}
///|
pub(all) struct DossierTimelineEvent {
date : String
actor : String
action : String
evidence_id : String
} derive(Eq, Debug)
///|
pub fn DossierTimelineEvent::DossierTimelineEvent(
date? : String = "",
actor? : String = "",
action? : String = "",
evidence_id? : String = "",
) -> DossierTimelineEvent {
{ date, actor, action, evidence_id }
}
///|
pub(all) struct DossierRisk {
id : String
title : String
level : String
mitigation : String
resolved : Bool
} derive(Eq, Debug)
///|
pub fn DossierRisk::DossierRisk(
id? : String = "",
title? : String = "",
level? : String = "medium",
mitigation? : String = "",
resolved? : Bool = false,
) -> DossierRisk {
{ id, title, level, mitigation, resolved }
}
///|
pub(all) struct DossierSection {
title : String
body : String
evidence_ids : Array[String]
} derive(Eq, Debug)
///|
pub fn DossierSection::DossierSection(
title? : String = "",
body? : String = "",
evidence_ids? : Array[String] = [],
) -> DossierSection {
{ title, body, evidence_ids }
}
///|
pub(all) struct DossierFinding {
id : String
severity : Severity
title : String
detail : String
action : String
evidence : String
penalty : Int
} derive(Eq, Debug)
///|
fn dossier_finding(
id : String,
severity : Severity,
title : String,
detail : String,
action : String,
evidence : String,
penalty : Int,
) -> DossierFinding {
{ id, severity, title, detail, action, evidence, penalty }
}
///|
pub(all) struct DossierProfile {
applicant : String
contact : String
project_title : String
package_name : String
repository : String
deadline : String
review_notice : String
relation_statement : String
evidence : Array[DossierEvidence]
timeline : Array[DossierTimelineEvent]
risks : Array[DossierRisk]
} derive(Eq, Debug)
///|
pub fn DossierProfile::DossierProfile(
applicant? : String = "",
contact? : String = "",
project_title? : String = "",
package_name? : String = "",
repository? : String = "",
deadline? : String = "",
review_notice? : String = "",
relation_statement? : String = "",
evidence? : Array[DossierEvidence] = [],
timeline? : Array[DossierTimelineEvent] = [],
risks? : Array[DossierRisk] = [],
) -> DossierProfile {
{
applicant,
contact,
project_title,
package_name,
repository,
deadline,
review_notice,
relation_statement,
evidence,
timeline,
risks,
}
}
///|
pub(all) struct DossierReport {
verdict : String
score : Int
ready : Bool
evidence_count : Int
complete_evidence_count : Int
missing_required_count : Int
unresolved_risk_count : Int
timeline_count : Int
action_count : Int
sections : Array[DossierSection]
actions : Array[String]
findings : Array[DossierFinding]
} derive(Eq, Debug)
///|
pub fn resubmission_dossier(profile : DossierProfile) -> DossierReport {
let findings = Array::new(capacity=12)
push_dossier_identity_findings(findings, profile)
push_dossier_relation_findings(findings, profile)
push_dossier_evidence_findings(findings, profile)
push_dossier_timeline_findings(findings, profile)
push_dossier_risk_findings(findings, profile)
let mut penalty = 0
let mut fail_count = 0
for finding in findings {
penalty += finding.penalty
if finding.severity == Fail {
fail_count += 1
}
}
let score = clamp_score(100 - penalty)
let missing = missing_required_evidence_count(profile.evidence)
let unresolved = unresolved_risk_count(profile.risks)
let complete = complete_evidence_count(profile.evidence)
let ready = fail_count == 0 && missing == 0 && unresolved == 0 && score >= 90
let verdict = if ready { "dossier-ready" } else { "needs-dossier-work" }
let actions = dossier_actions(profile, findings)
let sections = dossier_sections(profile)
{
verdict,
score,
ready,
evidence_count: profile.evidence.length(),
complete_evidence_count: complete,
missing_required_count: missing,
unresolved_risk_count: unresolved,
timeline_count: profile.timeline.length(),
action_count: actions.length(),
sections,
actions,
findings,
}
}
///|
pub fn resubmission_dossier_markdown(profile : DossierProfile) -> String {
resubmission_dossier(profile).to_markdown(profile)
}
///|
pub fn DossierReport::to_markdown(
self : DossierReport,
profile : DossierProfile,
) -> String {
let out = StringBuilder()
out.write_string("# ReviewProof Dossier\n\n")
out.write_string("- Applicant: " + profile.applicant + "\n")
out.write_string("- Contact: " + profile.contact + "\n")
out.write_string("- Project: " + profile.project_title + "\n")
out.write_string("- Package: " + profile.package_name + "\n")
out.write_string("- Repository: " + profile.repository + "\n")
out.write_string("- Deadline: " + profile.deadline + "\n")
out.write_string("- Verdict: " + self.verdict + "\n")
out.write_string("- Score: " + self.score.to_string() + "/100\n")
out.write_string(
"- Required evidence missing: " +
self.missing_required_count.to_string() +
"\n",
)
out.write_string(
"- Unresolved risks: " + self.unresolved_risk_count.to_string() + "\n\n",
)
out.write_string("## Review Notice\n\n")
out.write_string(blank_fallback(profile.review_notice, "No notice recorded."))
out.write_string("\n\n")
for section in self.sections {
out.write_string("## " + section.title + "\n\n")
out.write_string(section.body)
out.write_string("\n\n")
if section.evidence_ids.length() > 0 {
out.write_string("Evidence: ")
out.write_string(join_ids(section.evidence_ids))
out.write_string("\n\n")
}
}
out.write_string("## Evidence Matrix\n\n")
for item in profile.evidence {
let status = if item.complete { "complete" } else { "missing" }
let required = if item.required { "required" } else { "optional" }
out.write_string("- `" + item.id + "` " + status + " " + required)
out.write_string(": " + item.title)
if item.proof_path != "" {
out.write_string(" -- " + item.proof_path)
}
if item.summary != "" {
out.write_string(" Summary: " + item.summary)
}
out.write_string("\n")
}
out.write_string("\n## Timeline\n\n")
for event in profile.timeline {
out.write_string("- " + event.date + " " + event.actor + ": ")
out.write_string(event.action)
if event.evidence_id != "" {
out.write_string(" [" + event.evidence_id + "]")
}
out.write_string("\n")
}
out.write_string("\n## Risk Register\n\n")
for risk in profile.risks {
let status = if risk.resolved { "resolved" } else { "open" }
out.write_string("- `" + risk.id + "` " + risk.level + " " + status)
out.write_string(": " + risk.title)
if risk.mitigation != "" {
out.write_string(" Mitigation: " + risk.mitigation)
}
out.write_string("\n")
}
out.write_string("\n## Findings\n\n")
for finding in self.findings {
out.write_string("- [" + severity_text(finding.severity) + "] ")
out.write_string(finding.id + ": " + finding.title)
if finding.evidence != "" {
out.write_string(" -- " + finding.evidence)
}
if finding.action != "" && finding.severity != Pass {
out.write_string(" Action: " + finding.action)
}
out.write_string("\n")
}
if self.actions.length() > 0 {
out.write_string("\n## Required Actions\n\n")
for action in self.actions {
out.write_string("- " + action + "\n")
}
}
out.to_string()
}
///|
pub fn dossier_profile_from_review(feedback : ReviewFeedback) -> DossierProfile {
let evidence = Array::new(capacity=feedback.extension_evidence.length() + 4)
evidence.push(
DossierEvidence(
id="prior-work",
title="Related prior-work disclosure",
source="review notice and Mooncakes manifest",
proof_path="README.md / SUBMISSION.md / docs/review-response.md",
summary=prior_summary(feedback.prior_work),
required=true,
complete=feedback.prior_work.length() > 0,
),
)
evidence.push(
DossierEvidence(
id="relation",
title="Relation statement",
source="corrected proposal",
proof_path="SUBMISSION.md",
summary=feedback.relation_statement,
required=true,
complete=feedback.relation_statement != "",
),
)
evidence.push(
DossierEvidence(
id="review-report",
title="Reviewer-facing correction brief",
source="ReviewProof generated report",
proof_path="docs/review-response.md",
summary="Resubmission brief names the prior work and concrete extension scope.",
required=true,
complete=true,
),
)
for item in feedback.extension_evidence {
evidence.push(evidence_from_extension(item))
}
let timeline = [
DossierTimelineEvent(
date="2026-08-20",
actor="review committee",
action="Initial rejection notice reported high overlap.",
evidence_id="prior-work",
),
DossierTimelineEvent(
date="2026-08-20",
actor="applicant",
action="Project scope changed to review feedback correction and resubmission proof.",
evidence_id="relation",
),
DossierTimelineEvent(
date="2026-08-20",
actor="applicant",
action="MoonBit implementation, tests and review response documents were added.",
evidence_id="review-report",
),
]
let risks = [
DossierRisk(
id="overlap",
title="Old HarborCheck scope overlaps with related prior work",
level="high",
mitigation="Disclose EJJ-ai-nb/harborcheck and claim only the ReviewProof resubmission workflow as new scope.",
resolved=feedback.prior_work.length() > 0 &&
feedback.relation_statement != "",
),
DossierRisk(
id="renaming-only",
title="Correction could look like a rename without implementation",
level="high",
mitigation="Ship reviewproof.mbt, new tests, review-response docs and smoke output.",
resolved=implemented_extension_count(feedback.extension_evidence) >= 3,
),
DossierRisk(
id="material-gap",
title="Proposal could omit the extension relationship again",
level="medium",
mitigation="Keep README, SUBMISSION, research notes and review response in sync.",
resolved=feedback.relation_statement != "",
),
]
DossierProfile(
applicant="武步",
contact="19733880451 / wubu1234@qq.com",
project_title=feedback.project_title,
package_name=feedback.applicant_package,
repository=feedback.repository,
deadline=feedback.resubmit_deadline,
review_notice=feedback.notice,
relation_statement=feedback.relation_statement,
evidence~,
timeline~,
risks~,
)
}
///|
pub fn example_dossier_profile() -> DossierProfile {
dossier_profile_from_review(example_review_feedback())
}
///|
pub fn evidence_table_markdown(evidence : Array[DossierEvidence]) -> String {
let out = StringBuilder()
out.write_string("| id | status | required | proof | summary |\n")
out.write_string("| --- | --- | --- | --- | --- |\n")
for item in evidence {
out.write_string("| `" + item.id + "` | ")
out.write_string(if item.complete { "complete" } else { "missing" })
out.write_string(" | ")
out.write_string(if item.required { "yes" } else { "no" })
out.write_string(" | ")
out.write_string(table_cell(item.proof_path))
out.write_string(" | ")
out.write_string(table_cell(item.summary))
out.write_string(" |\n")
}
out.to_string()
}
///|
pub fn risk_register_markdown(risks : Array[DossierRisk]) -> String {
let out = StringBuilder()
out.write_string("| id | level | status | mitigation |\n")
out.write_string("| --- | --- | --- | --- |\n")
for risk in risks {
out.write_string("| `" + risk.id + "` | ")
out.write_string(risk.level + " | ")
out.write_string(if risk.resolved { "resolved" } else { "open" })
out.write_string(" | ")
out.write_string(table_cell(risk.mitigation))
out.write_string(" |\n")
}
out.to_string()
}
///|
pub fn timeline_markdown(events : Array[DossierTimelineEvent]) -> String {
let out = StringBuilder()
out.write_string("| date | actor | action | evidence |\n")
out.write_string("| --- | --- | --- | --- |\n")
for event in events {
out.write_string("| " + table_cell(event.date))
out.write_string(" | " + table_cell(event.actor))
out.write_string(" | " + table_cell(event.action))
out.write_string(" | " + table_cell(event.evidence_id))
out.write_string(" |\n")
}
out.to_string()
}
///|
pub fn dossier_summary_line(report : DossierReport) -> String {
"dossier=" +
report.verdict +
" score=" +
report.score.to_string() +
" evidence=" +
report.complete_evidence_count.to_string() +
"/" +
report.evidence_count.to_string() +
" missing=" +
report.missing_required_count.to_string() +
" risks=" +
report.unresolved_risk_count.to_string()
}
///|
fn push_dossier_identity_findings(
findings : Array[DossierFinding],
profile : DossierProfile,
) -> Unit {
if profile.applicant == "" || profile.contact == "" {
findings.push(
dossier_finding(
"dossier.applicant",
Fail,
"Applicant and contact are recorded",
"The dossier must identify the applicant and contact channel.",
"Fill applicant and contact fields.",
profile.applicant + " " + profile.contact,
15,
),
)
} else {
findings.push(
dossier_finding(
"dossier.applicant",
Pass,
"Applicant and contact are recorded",
"",
"",
profile.applicant,
0,
),
)
}
if profile.package_name == "" || profile.repository == "" {
findings.push(
dossier_finding(
"dossier.links",
Fail,
"Package and repository links are recorded",
"The dossier must tell reviewers where to inspect the corrected work.",
"Fill package_name and repository.",
profile.package_name + " " + profile.repository,
15,
),
)
} else {
findings.push(
dossier_finding(
"dossier.links",
Pass,
"Package and repository links are recorded",
"",
"",
profile.package_name + " / " + profile.repository,
0,
),
)
}
}
///|
fn push_dossier_relation_findings(
findings : Array[DossierFinding],
profile : DossierProfile,
) -> Unit {
if profile.review_notice == "" {
findings.push(
dossier_finding(
"dossier.notice",
Warn,
"Review notice is recorded",
"The dossier should quote or summarize the rejection reason.",
"Add the review notice summary.",
"",
5,
),
)
} else {
findings.push(
dossier_finding(
"dossier.notice",
Pass,
"Review notice is recorded",
"",
"",
profile.review_notice,
0,
),
)
}
if profile.relation_statement == "" {
findings.push(
dossier_finding(
"dossier.relation",
Fail,
"Relation and extension statement is recorded",
"The dossier must explain how this project differs from related prior work.",
"Add a relation statement.",
"",
20,
),
)
} else if !contains_any(profile.relation_statement, [
"related", "prior", "EJJ-ai-nb/harborcheck", "扩展", "关系",
]) {
findings.push(
dossier_finding(
"dossier.relation",
Warn,
"Relation and extension statement is explicit",
"The relation statement exists but does not clearly name prior work or extension.",
"Mention the prior package and the new extension boundary.",
profile.relation_statement,
8,
),
)
} else {
findings.push(
dossier_finding(
"dossier.relation",
Pass,
"Relation and extension statement is recorded",
"",
"",
profile.relation_statement,
0,
),
)
}
}
///|
fn push_dossier_evidence_findings(
findings : Array[DossierFinding],
profile : DossierProfile,
) -> Unit {
let missing = missing_required_evidence_count(profile.evidence)
let complete = complete_evidence_count(profile.evidence)
if profile.evidence.length() == 0 {
findings.push(
dossier_finding(
"dossier.evidence",
Fail,
"Evidence matrix is present",
"A dossier without evidence cannot support resubmission.",
"Add required DossierEvidence records.",
"",
25,
),
)
} else if missing > 0 {
findings.push(
dossier_finding(
"dossier.evidence",
Fail,
"Required evidence is complete",
"Some required evidence records are missing proof.",
"Complete required evidence before resubmission.",
missing.to_string() + " missing required records",
18 + missing * 5,
),
)
} else {
findings.push(
dossier_finding(
"dossier.evidence",
Pass,
"Required evidence is complete",
"",
"",
complete.to_string() + "/" + profile.evidence.length().to_string(),
0,
),
)
}
if complete < 5 {
findings.push(
dossier_finding(
"dossier.evidence-depth",
Warn,
"Evidence matrix has enough depth",
"A stronger resubmission should include at least five complete records.",
"Add tests, docs, package and CI evidence records.",
complete.to_string() + " complete records",
5,
),
)
} else {
findings.push(
dossier_finding(
"dossier.evidence-depth",
Pass,
"Evidence matrix has enough depth",
"",
"",
complete.to_string() + " complete records",
0,
),
)
}
}
///|
fn push_dossier_timeline_findings(
findings : Array[DossierFinding],
profile : DossierProfile,
) -> Unit {
if profile.timeline.length() < 3 {
findings.push(
dossier_finding(
"dossier.timeline",
Warn,
"Correction timeline is traceable",
"A timeline helps reviewers see the correction process.",
"Record rejection, implementation and publication events.",
profile.timeline.length().to_string() + " events",
5,
),
)
} else {
findings.push(
dossier_finding(
"dossier.timeline",
Pass,
"Correction timeline is traceable",
"",
"",
profile.timeline.length().to_string() + " events",
0,
),
)
}
let missing_links = timeline_missing_evidence_count(profile.timeline)
if missing_links > 0 {
findings.push(
dossier_finding(
"dossier.timeline-links",
Warn,
"Timeline events link to evidence",
"Some timeline events do not reference evidence ids.",
"Add evidence_id to timeline events.",
missing_links.to_string() + " events",
missing_links * 2,
),
)
} else {
findings.push(
dossier_finding(
"dossier.timeline-links",
Pass,
"Timeline events link to evidence",
"",
"",
"all events linked",
0,
),
)
}
}
///|
fn push_dossier_risk_findings(
findings : Array[DossierFinding],
profile : DossierProfile,
) -> Unit {
let unresolved = unresolved_risk_count(profile.risks)
let high = unresolved_high_risk_count(profile.risks)
if profile.risks.length() == 0 {
findings.push(
dossier_finding(
"dossier.risks",
Warn,
"Risk register is present",
"A resubmission dossier should show how overlap risk was handled.",
"Add risk records for overlap, renaming-only and material gaps.",
"",
6,
),
)
} else if high > 0 {
findings.push(
dossier_finding(
"dossier.risks",
Fail,
"High risks are resolved",
"Open high risks mean the resubmission may be rejected again.",
"Resolve high-risk items before resubmission.",
high.to_string() + " open high risks",
20 + high * 5,
),
)
} else if unresolved > 0 {
findings.push(
dossier_finding(
"dossier.risks",
Warn,
"All risks are resolved",
"Some non-high risks remain open.",
"Resolve or explain remaining risks.",
unresolved.to_string() + " open risks",
5,
),
)
} else {
findings.push(
dossier_finding(
"dossier.risks",
Pass,
"All risks are resolved",
"",
"",
profile.risks.length().to_string() + " resolved risks",
0,
),
)
}
}
///|
fn dossier_sections(profile : DossierProfile) -> Array[DossierSection] {
[
DossierSection(
title="Correction Position",
body=correction_position(profile),
evidence_ids=["prior-work", "relation"],
),
DossierSection(
title="Implemented Difference",
body=implemented_difference(profile.evidence),
evidence_ids=complete_evidence_ids(profile.evidence),
),
DossierSection(title="Reviewer Action", body=reviewer_action(profile), evidence_ids=[
"review-report",
]),
]
}
///|
fn correction_position(profile : DossierProfile) -> String {
let out = StringBuilder()
out.write_string(
"The corrected submission treats the old overlapping checklist scope as related prior work. ",
)
if profile.relation_statement != "" {
out.write_string(profile.relation_statement)
} else {
out.write_string("A relation statement still needs to be added.")
}
out.to_string()
}
///|
fn implemented_difference(evidence : Array[DossierEvidence]) -> String {
let out = StringBuilder()
out.write_string(
"The implementation evidence matrix contains " +
complete_evidence_count(evidence).to_string() +
" complete records. ",
)
out.write_string(
"The strongest records should point to MoonBit code, tests, docs and package metadata.",
)
out.to_string()
}
///|
fn reviewer_action(profile : DossierProfile) -> String {
"Review the corrected package " +
profile.package_name +
" in repository " +
profile.repository +
" and compare the documented extension boundary against the disclosed prior work."
}
///|
fn dossier_actions(
profile : DossierProfile,
findings : Array[DossierFinding],
) -> Array[String] {
let actions = Array::new(capacity=8)
for finding in findings {
if finding.severity != Pass && finding.action != "" {
actions.push(finding.action)
}
}
for item in profile.evidence {
if item.required && !item.complete {
actions.push("Complete required evidence `" + item.id + "`.")
}
}
for risk in profile.risks {
if !risk.resolved {
actions.push("Resolve risk `" + risk.id + "`: " + risk.title + ".")
}
}
actions
}
///|
fn evidence_from_extension(item : ExtensionEvidence) -> DossierEvidence {
DossierEvidence(
id=slug_from_title(item.feature),
title=item.feature,
source="extension evidence",
proof_path=item.proof,
summary=item.value,
required=true,
complete=item.implemented && item.implementation != "" && item.proof != "",
)
}
///|
fn complete_evidence_ids(evidence : Array[DossierEvidence]) -> Array[String] {
let ids = Array::new(capacity=evidence.length())
for item in evidence {
if item.complete {
ids.push(item.id)
}
}
ids
}
///|
fn complete_evidence_count(evidence : Array[DossierEvidence]) -> Int {
let mut count = 0
for item in evidence {
if item.complete {
count += 1
}
}
count
}
///|
fn missing_required_evidence_count(evidence : Array[DossierEvidence]) -> Int {
let mut count = 0
for item in evidence {
if item.required && !item.complete {
count += 1
}
}
count
}
///|
fn unresolved_risk_count(risks : Array[DossierRisk]) -> Int {
let mut count = 0
for risk in risks {
if !risk.resolved {
count += 1
}
}
count
}
///|
fn unresolved_high_risk_count(risks : Array[DossierRisk]) -> Int {
let mut count = 0
for risk in risks {
if !risk.resolved && lower(risk.level) == "high" {
count += 1
}
}
count
}
///|
fn timeline_missing_evidence_count(events : Array[DossierTimelineEvent]) -> Int {
let mut count = 0
for event in events {
if event.evidence_id == "" {
count += 1
}
}
count
}
///|
fn prior_summary(prior_work : Array[PriorWork]) -> String {
if prior_work.length() == 0 {
return "No related prior work recorded."
}
let out = StringBuilder()
for i in 0.. 0 {
out.write_string("; ")
}
let prior = prior_work[i]
out.write_string(prior.package_name)
if prior.version != "" {
out.write_string("@" + prior.version)
}
if prior.overlap != "" {
out.write_string(" overlap=" + prior.overlap)
}
}
out.to_string()
}
///|
fn join_ids(ids : Array[String]) -> String {
let out = StringBuilder()
for i in 0.. 0 {
out.write_string(", ")
}
out.write_string("`" + ids[i] + "`")
}
out.to_string()
}
///|
fn blank_fallback(text : String, fallback : String) -> String {
if text == "" {
fallback
} else {
text
}
}
///|
fn table_cell(text : String) -> String {
text.replace_all(old="|", new="\\|").replace_all(old="\n", new="
")
}
///|
fn slug_from_title(title : String) -> String {
let out = StringBuilder()
let lower_title = lower(title)
let mut previous_dash = false
for ch in lower_title {
let code = ch.to_int()
let digit = code >= 48 && code <= 57
let alpha = code >= 97 && code <= 122
if digit || alpha {
out.write_char(ch)
previous_dash = false
} else if !previous_dash {
out.write_string("-")
previous_dash = true
}
}
trim_dash(out.to_string())
}
///|
fn trim_dash(text : String) -> String {
let mut start = 0
let mut end = text.length()
while start < end && text[start:start + 1] == "-" {
start += 1
}
while end > start && text[end - 1:end] == "-" {
end -= 1
}
if start >= end {
"evidence"
} else {
text[start:end].to_owned()
}
}