///|
/// Evidence Capsule converts an Intent Gate report into a deterministic
/// release-review artifact. It gives reviewers a stable fingerprint, a
/// remediation checklist, and a concise non-overlap statement that sits above
/// ordinary robots.txt parsing or crawl planning.
pub(all) enum CapsuleReadiness {
CapsuleReady
CapsuleNeedsReview
CapsuleBlocked
} derive(Eq, Debug)
///|
pub(all) enum RemediationSeverity {
RemediationInfo
RemediationWarning
RemediationCritical
} derive(Eq, Debug)
///|
pub(all) enum RemediationKind {
RemediationNoIntent
RemediationRobotsBlock
RemediationExplicitAuthorization
RemediationMissingEvidence
RemediationHumanReview
RemediationHighRisk
RemediationRetention
} derive(Eq, Debug)
///|
pub(all) struct RemediationItem {
id : String
intent_label : String
kind : RemediationKind
severity : RemediationSeverity
verdict : IntentVerdict
action : String
evidence_hint : String
} derive(Eq, Debug)
///|
pub(all) struct EvidenceCapsule {
project_name : String
version : String
fingerprint : String
readiness : CapsuleReadiness
source_summary : String
novelty_statement : String
reviewer_note : String
checklist : Array[String]
remediation_items : Array[RemediationItem]
report : IntentGateReport
} derive(Eq, Debug)
///|
pub fn CapsuleReadiness::label(self : CapsuleReadiness) -> String {
match self {
CapsuleReady => "ready"
CapsuleNeedsReview => "needs-review"
CapsuleBlocked => "blocked"
}
}
///|
pub fn RemediationSeverity::label(self : RemediationSeverity) -> String {
match self {
RemediationInfo => "info"
RemediationWarning => "warning"
RemediationCritical => "critical"
}
}
///|
pub fn RemediationKind::label(self : RemediationKind) -> String {
match self {
RemediationNoIntent => "no-intent"
RemediationRobotsBlock => "robots-block"
RemediationExplicitAuthorization => "explicit-authorization"
RemediationMissingEvidence => "missing-evidence"
RemediationHumanReview => "human-review"
RemediationHighRisk => "high-risk"
RemediationRetention => "retention"
}
}
///|
pub fn build_evidence_capsule(
report : IntentGateReport,
project_name : StringView,
version : StringView,
) -> EvidenceCapsule {
let project = project_name.to_owned()
let release_version = version.to_owned()
let remediation_items = capsule_remediations(report)
let readiness = capsule_readiness(report, remediation_items)
let source_summary = capsule_source_summary(report)
let novelty_statement = capsule_novelty_statement()
let reviewer_note = capsule_reviewer_note(readiness, remediation_items)
let checklist = capsule_checklist(report, readiness)
let fingerprint = capsule_fingerprint(report, project, release_version)
{
project_name: project,
version: release_version,
fingerprint,
readiness,
source_summary,
novelty_statement,
reviewer_note,
checklist,
remediation_items,
report,
}
}
///|
pub fn IntentGateReport::to_evidence_capsule(
self : IntentGateReport,
project_name : StringView,
version : StringView,
) -> EvidenceCapsule {
build_evidence_capsule(self, project_name, version)
}
///|
pub fn EvidenceCapsule::is_release_ready(self : EvidenceCapsule) -> Bool {
self.readiness == CapsuleReady && self.remediation_items.is_empty()
}
///|
pub fn EvidenceCapsule::has_remediation(
self : EvidenceCapsule,
kind : RemediationKind,
) -> Bool {
self.remediation_items.any(item => item.kind == kind)
}
///|
pub fn EvidenceCapsule::to_line(self : EvidenceCapsule) -> String {
self.project_name +
"@" +
self.version +
" readiness=" +
self.readiness.label() +
" fingerprint=" +
self.fingerprint +
" remediations=" +
self.remediation_items.length().to_string()
}
///|
pub fn EvidenceCapsule::to_markdown(self : EvidenceCapsule) -> String {
let lines : Array[String] = []
lines.push("# RoboPolicy Evidence Capsule")
lines.push("")
lines.push("- Project: " + self.project_name)
lines.push("- Version: " + self.version)
lines.push("- Fingerprint: " + self.fingerprint)
lines.push("- Readiness: " + self.readiness.label())
lines.push("- Source: " + self.source_summary)
lines.push("- Non-overlap: " + self.novelty_statement)
lines.push("- Reviewer note: " + self.reviewer_note)
lines.push("")
lines.push("## Release Checklist")
lines.push("")
for item in self.checklist {
lines.push("- " + item)
}
lines.push("")
lines.push("## Remediation Items")
lines.push("")
if self.remediation_items.is_empty() {
lines.push("- none")
} else {
lines.push(
"| ID | Intent | Kind | Severity | Verdict | Action | Evidence |",
)
lines.push("| --- | --- | --- | --- | --- | --- | --- |")
for item in self.remediation_items {
lines.push(item.to_markdown_row())
}
}
lines.join("\n")
}
///|
pub fn EvidenceCapsule::to_json(self : EvidenceCapsule) -> String {
"{" +
"\"project\":\"" +
capsule_json_escape(self.project_name) +
"\",\"version\":\"" +
capsule_json_escape(self.version) +
"\",\"fingerprint\":\"" +
capsule_json_escape(self.fingerprint) +
"\",\"readiness\":\"" +
self.readiness.label() +
"\",\"source_summary\":\"" +
capsule_json_escape(self.source_summary) +
"\",\"novelty_statement\":\"" +
capsule_json_escape(self.novelty_statement) +
"\",\"reviewer_note\":\"" +
capsule_json_escape(self.reviewer_note) +
"\",\"checklist\":" +
capsule_string_array_to_json(self.checklist) +
",\"remediation_items\":" +
capsule_remediations_to_json(self.remediation_items) +
",\"intent_summary\":" +
self.report.summary.to_json() +
"}"
}
///|
pub fn RemediationItem::to_markdown_row(self : RemediationItem) -> String {
"| " +
capsule_cell(self.id) +
" | " +
capsule_cell(self.intent_label) +
" | " +
self.kind.label() +
" | " +
self.severity.label() +
" | " +
self.verdict.label() +
" | " +
capsule_cell(self.action) +
" | " +
capsule_cell(self.evidence_hint) +
" |"
}
///|
pub fn RemediationItem::to_json(self : RemediationItem) -> String {
"{" +
"\"id\":\"" +
capsule_json_escape(self.id) +
"\",\"intent\":\"" +
capsule_json_escape(self.intent_label) +
"\",\"kind\":\"" +
self.kind.label() +
"\",\"severity\":\"" +
self.severity.label() +
"\",\"verdict\":\"" +
self.verdict.label() +
"\",\"action\":\"" +
capsule_json_escape(self.action) +
"\",\"evidence_hint\":\"" +
capsule_json_escape(self.evidence_hint) +
"\"}"
}
///|
pub fn sample_evidence_capsule_markdown() -> String {
review_intents(parse(sample_robots()), sample_intents())
.to_evidence_capsule("RoboPolicy Intent Gate", "0.2.0")
.to_markdown()
}
///|
fn capsule_remediations(report : IntentGateReport) -> Array[RemediationItem] {
let items : Array[RemediationItem] = []
if report.summary.total_intents == 0 {
items.push(
remediation(
"CAP-000",
"release",
RemediationNoIntent,
RemediationCritical,
IntentDenied,
"add at least one crawl intent before release review",
"intent-list",
),
)
}
for review in report.reviews {
if !review.robots_allowed {
items.push(
remediation_for_review(
review,
RemediationRobotsBlock,
RemediationCritical,
"remove the target or obtain an updated robots policy that permits it",
"robots-snapshot",
),
)
}
if review.intent.needs_explicit_authorization() &&
!review.intent.has_explicit_authorization() {
items.push(
remediation_for_review(
review,
RemediationExplicitAuthorization,
RemediationCritical,
"attach owner approval, contractual consent, or internal-test scope",
"authorization-ticket",
),
)
}
if !review.intent.has_evidence() {
items.push(
remediation_for_review(
review,
RemediationMissingEvidence,
RemediationWarning,
"add a reviewer-visible evidence id for this intent",
"evidence-id",
),
)
}
if review.requires_human_review {
items.push(
remediation_for_review(
review,
RemediationHumanReview,
RemediationWarning,
"record a human release decision before running the crawler",
"review-record",
),
)
}
if review.risk_band == IntentRiskOrange || review.risk_band == IntentRiskRed {
items.push(
remediation_for_review(
review,
RemediationHighRisk,
RemediationWarning,
"lower sensitivity, shorten retention, or split this intent into a safer scope",
"risk-review",
),
)
}
if review.intent.retention_days > 90 &&
review.intent.consent != ConsentContractual {
items.push(
remediation_for_review(
review,
RemediationRetention,
RemediationWarning,
"reduce retention to 90 days or attach contractual consent",
"retention-plan",
),
)
}
}
items
}
///|
fn capsule_readiness(
report : IntentGateReport,
remediation_items : Array[RemediationItem],
) -> CapsuleReadiness {
if report.summary.total_intents == 0 || report.summary.denied_intents > 0 {
CapsuleBlocked
} else if report.summary.review_intents > 0 ||
report.summary.high_risk_intents > 0 ||
report.summary.missing_evidence > 0 ||
!remediation_items.is_empty() {
CapsuleNeedsReview
} else {
CapsuleReady
}
}
///|
fn capsule_source_summary(report : IntentGateReport) -> String {
report.summary.to_line() +
"; targets=" +
report.target_summary.total_targets.to_string() +
"; target-score=" +
report.target_summary.score.to_string()
}
///|
fn capsule_novelty_statement() -> String {
"captures AI intent verdicts, authorization evidence, remediation gaps, and a stable release fingerprint above robots access checks"
}
///|
fn capsule_reviewer_note(
readiness : CapsuleReadiness,
remediation_items : Array[RemediationItem],
) -> String {
match readiness {
CapsuleReady =>
"ready to attach to CI, release notes, and acceptance materials"
CapsuleNeedsReview =>
"resolve " +
remediation_items.length().to_string() +
" review item(s) before production release"
CapsuleBlocked =>
"blocked until denied intents or missing intent evidence are corrected"
}
}
///|
fn capsule_checklist(
report : IntentGateReport,
readiness : CapsuleReadiness,
) -> Array[String] {
let checklist : Array[String] = []
checklist.push("MoonBit report generated from deterministic offline inputs")
checklist.push("Intent verdicts recorded: " + report.summary.to_line())
checklist.push(
"Robots evidence attached for " +
report.summary.total_intents.to_string() +
" intent(s)",
)
checklist.push("Release readiness: " + readiness.label())
if report.summary.explicit_authorization > 0 {
checklist.push("At least one intent records explicit authorization")
} else {
checklist.push("No explicit authorization recorded yet")
}
if report.summary.missing_evidence == 0 {
checklist.push("Every intent has a reusable evidence id")
} else {
checklist.push(
report.summary.missing_evidence.to_string() +
" intent(s) still need reusable evidence id",
)
}
checklist
}
///|
fn capsule_fingerprint(
report : IntentGateReport,
project_name : String,
version : String,
) -> String {
let seed = capsule_fingerprint_seed(report, project_name, version)
"rpec-" +
seed.length().to_string() +
"-" +
capsule_checksum(seed).to_string() +
"-" +
report.summary.score.to_string()
}
///|
fn capsule_fingerprint_seed(
report : IntentGateReport,
project_name : String,
version : String,
) -> String {
let lines : Array[String] = []
lines.push(project_name)
lines.push(version)
lines.push(report.summary.to_line())
lines.push(report.target_summary.to_line())
for review in report.reviews {
lines.push(review.evidence_line)
lines.push(review.risk_band.label())
lines.push(review.verdict.label())
lines.push(review.blockers.join(";"))
lines.push(review.obligations.join(";"))
}
lines.join("\n")
}
///|
fn capsule_checksum(text : String) -> Int {
let mut sum = 5381
let mut index = 1
for ch in text {
sum = sum * 17 + capsule_char_weight(ch) + index
while sum > 100000000 {
sum = sum - 99998311
}
index = index + 1
}
if sum < 0 {
0 - sum
} else {
sum
}
}
///|
fn capsule_char_weight(ch : Char) -> Int {
match ch {
'a' => 11
'e' => 13
'i' => 17
'o' => 19
'u' => 23
'r' => 29
's' => 31
't' => 37
'n' => 41
'p' => 43
'd' => 47
'c' => 53
'm' => 59
'g' => 61
'/' => 67
'-' => 71
'_' => 73
':' => 79
';' => 83
'|' => 89
'\n' => 97
'0' => 101
'1' => 103
'2' => 107
'3' => 109
'4' => 113
'5' => 127
'6' => 131
'7' => 137
'8' => 139
'9' => 149
_ => 151
}
}
///|
fn remediation_for_review(
review : IntentReview,
kind : RemediationKind,
severity : RemediationSeverity,
action : StringView,
evidence_hint : StringView,
) -> RemediationItem {
remediation(
"CAP-" + review.intent.label + "-" + kind.label(),
review.intent.label,
kind,
severity,
review.verdict,
action,
evidence_hint,
)
}
///|
fn remediation(
id : StringView,
intent_label : StringView,
kind : RemediationKind,
severity : RemediationSeverity,
verdict : IntentVerdict,
action : StringView,
evidence_hint : StringView,
) -> RemediationItem {
{
id: id.to_owned(),
intent_label: intent_label.to_owned(),
kind,
severity,
verdict,
action: action.to_owned(),
evidence_hint: evidence_hint.to_owned(),
}
}
///|
fn capsule_remediations_to_json(items : Array[RemediationItem]) -> String {
let rows : Array[String] = []
for item in items {
rows.push(item.to_json())
}
"[" + rows.join(",") + "]"
}
///|
fn capsule_string_array_to_json(values : Array[String]) -> String {
let rows : Array[String] = []
for value in values {
rows.push("\"" + capsule_json_escape(value) + "\"")
}
"[" + rows.join(",") + "]"
}
///|
fn capsule_cell(value : String) -> String {
value.replace_all(old="|", new="\\|")
}
///|
fn capsule_json_escape(value : String) -> String {
value
.replace_all(old="\\", new="\\\\")
.replace_all(old="\"", new="\\\"")
.replace_all(old="\n", new="\\n")
.replace_all(old="\r", new="\\r")
.replace_all(old="\t", new="\\t")
}