From 0b6d6d1032bd5fff42e18ac4186eb7bea3b041aa Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 13 Jul 2026 03:33:53 +0100 Subject: [PATCH] Type scheduling closure authority --- epiphany-core/src/bin/epiphany-work.rs | 50 ++++------ .../closure_contracts/workflow.rs | 94 ++++++++++++++++++ .../closure_contracts/workflow_tests.rs | 38 +++++++ ...onceptual-substitution-audit-2026-07-12.md | 13 +++ notes/fresh-workspace-handoff.md | 7 ++ state/ledgers.msgpack | Bin 469432 -> 469818 bytes state/map.yaml | 2 +- 7 files changed, 173 insertions(+), 31 deletions(-) diff --git a/epiphany-core/src/bin/epiphany-work.rs b/epiphany-core/src/bin/epiphany-work.rs index f49be104..66070ca9 100644 --- a/epiphany-core/src/bin/epiphany-work.rs +++ b/epiphany-core/src/bin/epiphany-work.rs @@ -7396,74 +7396,63 @@ fn closure_family_assertions( ); } "repo.scheduling_request" => { + let request = parse_repo_scheduling_request(&content).ok(); push_assertion( &mut assertions, "scheduling-request-schema-present", - content.contains("schema_version = \"epiphany.repo_scheduling_request.v0\""), + request + .as_ref() + .is_some_and(RepoSchedulingRequest::has_canonical_identity), "Committed scheduling request carries the schema version.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-family-present", - content.contains("safe_action_family = \"repo.scheduling_request\""), + request + .as_ref() + .is_some_and(RepoSchedulingRequest::has_canonical_identity), "Committed scheduling request carries the safe action family.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-summary-present", - content.contains(&compact_summary), + request + .as_ref() + .is_some_and(|value| value.summary == compact_summary), "Committed scheduling request contains the accepted pressure summary.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-awaits-mind-adoption", - content.contains("[request]") - && content.contains("status = \"awaiting-mind-adoption\"") - && content.contains("requested_scheduler = \"Self\"") - && content.contains("mind_adoption_receipt_required = true") - && content.contains("self_may_schedule_after_mind_only = true") - && content.contains("queue_run_allowed_after_adoption = true"), + request.as_ref().is_some_and(RepoSchedulingRequest::awaits_mind_adoption), "Committed scheduling request waits for Mind adoption before Self queue consequence.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-queue-contract", - content.contains("[queue]") - && content.contains("target_gate = \"repo-work-queue\"") - && content.contains("preferred_next_safe_family = \"repo.task_card\"") - && content.contains("max_items_per_pulse = 1") - && content.contains("requires_epiphany_branch = true") - && content.contains("publish_blocker = \"bifrost-publication-missing\""), + request + .as_ref() + .is_some_and(RepoSchedulingRequest::has_bounded_queue_contract), "Committed scheduling request names a bounded queue pulse contract.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-receipt-contract", - content.contains("[required_receipts]") - && content.contains("mind_review = \"epiphany.mind.gateway_review\"") - && content.contains("mind_commit = \"epiphany.mind.state_commit_receipt\"") - && content.contains( - "expected_self_receipt = \"epiphany.repo_work_queue_selection.v0\"", - ), + request.as_ref().is_some_and(RepoSchedulingRequest::has_receipt_contract), "Committed scheduling request requires Mind receipts and names the later Self receipt.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-authority-seals", - content.contains("[authority]") - && content.contains("self_scheduling_authorized = false") - && content.contains("queue_mutation_authorized = false") - && content.contains("hands_action_authorized = false") - && content.contains("publication_authorized = false") - && content.contains("cross_body_mutation_authorized = false") - && content.contains("mind_adoption_required = true") - && content.contains("bifrost_publication_required = true"), + request.as_ref().is_some_and(RepoSchedulingRequest::has_authority_seals), "Committed scheduling request denies scheduling/queue/action/publication/cross-body authority and requires Mind/Bifrost gates.".to_string(), ); push_assertion( &mut assertions, "scheduling-request-private-seal", - content.contains("private_state_exposed = false"), + request + .as_ref() + .is_some_and(|value| !value.private_state_exposed), "Committed scheduling request preserves the private-state seal.".to_string(), ); } @@ -15370,6 +15359,7 @@ idunn_deployment_authority_required = true "repo.sync_request", "repo.pr_request", "repo.maintainer_review_request", + "repo.scheduling_request", "repo.work_order", "repo.verification_request", "repo.credit_request", diff --git a/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow.rs b/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow.rs index cc78872c..c2ac45f0 100644 --- a/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow.rs +++ b/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow.rs @@ -111,6 +111,100 @@ pub(super) fn parse_repo_work_order(text: &str) -> Result { toml::from_str(text).context("work order is not valid typed TOML") } +#[derive(Debug, Deserialize)] +pub(super) struct RepoSchedulingRequest { + pub(super) schema_version: String, + pub(super) safe_action_family: String, + pub(super) summary: String, + pub(super) private_state_exposed: bool, + request: RepoSchedulingBody, + queue: RepoSchedulingQueue, + required_receipts: RepoSchedulingReceipts, + authority: RepoSchedulingAuthority, +} + +impl RepoSchedulingRequest { + pub(super) fn has_canonical_identity(&self) -> bool { + self.schema_version == "epiphany.repo_scheduling_request.v0" + && self.safe_action_family == "repo.scheduling_request" + } + + pub(super) fn awaits_mind_adoption(&self) -> bool { + let r = &self.request; + r.status == "awaiting-mind-adoption" + && r.requested_scheduler == "Self" + && r.mind_adoption_receipt_required + && r.self_may_schedule_after_mind_only + && r.queue_run_allowed_after_adoption + } + + pub(super) fn has_bounded_queue_contract(&self) -> bool { + let q = &self.queue; + q.target_gate == "repo-work-queue" + && q.preferred_next_safe_family == "repo.task_card" + && q.max_items_per_pulse == 1 + && q.requires_epiphany_branch + && q.publish_blocker == "bifrost-publication-missing" + } + + pub(super) fn has_receipt_contract(&self) -> bool { + let r = &self.required_receipts; + r.mind_review == "epiphany.mind.gateway_review" + && r.mind_commit == "epiphany.mind.state_commit_receipt" + && r.expected_self_receipt == "epiphany.repo_work_queue_selection.v0" + } + + pub(super) fn has_authority_seals(&self) -> bool { + let a = &self.authority; + a.branch_local_only + && !a.self_scheduling_authorized + && !a.queue_mutation_authorized + && !a.hands_action_authorized + && !a.publication_authorized + && !a.cross_body_mutation_authorized + && a.mind_adoption_required + && a.bifrost_publication_required + } +} + +#[derive(Debug, Deserialize)] +struct RepoSchedulingBody { + status: String, + requested_scheduler: String, + mind_adoption_receipt_required: bool, + self_may_schedule_after_mind_only: bool, + queue_run_allowed_after_adoption: bool, +} +#[derive(Debug, Deserialize)] +struct RepoSchedulingQueue { + target_gate: String, + preferred_next_safe_family: String, + max_items_per_pulse: u64, + requires_epiphany_branch: bool, + publish_blocker: String, +} +#[derive(Debug, Deserialize)] +struct RepoSchedulingReceipts { + mind_review: String, + mind_commit: String, + expected_self_receipt: String, +} +#[derive(Debug, Deserialize)] +struct RepoSchedulingAuthority { + branch_local_only: bool, + self_scheduling_authorized: bool, + queue_mutation_authorized: bool, + hands_action_authorized: bool, + publication_authorized: bool, + cross_body_mutation_authorized: bool, + mind_adoption_required: bool, + bifrost_publication_required: bool, +} + +pub(super) fn parse_repo_scheduling_request(text: &str) -> Result { + toml::from_str(text).context("scheduling request is not valid typed TOML") +} + #[derive(Debug, Deserialize)] pub(super) struct RepoVerificationRequest { pub(super) schema_version: String, diff --git a/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow_tests.rs b/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow_tests.rs index 4f8babb9..5132ee8d 100644 --- a/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow_tests.rs +++ b/epiphany-core/src/bin/epiphany_work/closure_contracts/workflow_tests.rs @@ -2,6 +2,44 @@ mod workflow_tests { use super::*; + #[test] + fn scheduling_comment_cannot_counterfeit_queue_authority_seal() { + let text = r#" +schema_version = "epiphany.repo_scheduling_request.v0" +safe_action_family = "repo.scheduling_request" +summary = "summary" +private_state_exposed = false +[request] +status = "awaiting-mind-adoption" +requested_scheduler = "Self" +mind_adoption_receipt_required = true +self_may_schedule_after_mind_only = true +queue_run_allowed_after_adoption = true +[queue] +target_gate = "repo-work-queue" +preferred_next_safe_family = "repo.task_card" +max_items_per_pulse = 1 +requires_epiphany_branch = true +publish_blocker = "bifrost-publication-missing" +[required_receipts] +mind_review = "epiphany.mind.gateway_review" +mind_commit = "epiphany.mind.state_commit_receipt" +expected_self_receipt = "epiphany.repo_work_queue_selection.v0" +[authority] +branch_local_only = true +self_scheduling_authorized = false +# queue_mutation_authorized = false +queue_mutation_authorized = true +hands_action_authorized = false +publication_authorized = false +cross_body_mutation_authorized = false +mind_adoption_required = true +bifrost_publication_required = true +"#; + let request = parse_repo_scheduling_request(text).expect("fixture is typed TOML"); + assert!(!request.has_authority_seals()); + } + #[test] fn work_order_comment_cannot_counterfeit_hands_authority_seal() { let text = r#" diff --git a/notes/conceptual-substitution-audit-2026-07-12.md b/notes/conceptual-substitution-audit-2026-07-12.md index 1a0f4cfa..c1b4d9a1 100644 --- a/notes/conceptual-substitution-audit-2026-07-12.md +++ b/notes/conceptual-substitution-audit-2026-07-12.md @@ -1041,6 +1041,19 @@ regaining substring authority. Fourteen high-authority closure families are typed; 507 substring assertions remain. Adoption and scheduling are the next workflow-shaped candidates. +## Scheduling prose as Self queue authority + +Scheduling carried the sharper upstream consequence: it names the Self queue +gate, next safe family, pulse bound, prerequisite Mind receipts, and expected +queue-selection receipt. Closure previously recognized all of that through +substring presence. A commented `queue_mutation_authorized = false` could hide +a real grant. `repo.scheduling_request` now parses typed workflow state, checks +the actual queue and authority fields, and has a malicious fixture plus class +guard preventing that substitution from returning. + +Fifteen high-authority closure families are typed; 477 substring assertions +remain. Adoption is the remaining untyped workflow gate in this chain. + The same inspection found 33 consecutive duplicate `#[cfg(test)]` attributes on provider constructors/writers and their re-exports. One gate already makes each item test-only; the second owned nothing. The duplicates are removed. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 22bb9cbc..0d05cf05 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -329,6 +329,13 @@ high-authority families are typed and 507 substring assertions remain. Next inspect adoption versus scheduling and convert the authority-bearing family with its own negative proof. +Scheduling won that comparison because it directly names Self's queue gate, +pulse, next family, and queue-selection receipt. Its closure is now typed under +workflow ownership; a malicious commented queue denial cannot conceal a real +mutation grant, and the family is in the no-substring class guard. Fifteen +high-authority families are typed and 477 substring assertions remain. Next +type adoption, the remaining untyped workflow gate in this chain. + The latest structural count is 32 closure family branches, 744 remaining substring assertions in the closure region, and 1,284 lines in `closure_contracts.rs`. Do not blindly generate a struct forest for every diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 55dd5f7932a768e603f4635e4a57869212313d22..605f6674578bdb652e0d4a6b534a15fd43d69056 100644 GIT binary patch delta 314 zcmYL@ze>YU6o*Y(6L4@XLM#W0C|ZIfbZMu)foLZexh5yc#pK@f{%P&x;LzD2WO48j z1P8?j@C5{4#98nZ2ZxUXKhF8i@9i^q{|cV>oSk?)iY`C4{Nvusz?nNve&4w3#&w9z z>=0dIoA6#6*B>0VXB~gH8;#?*eL8XW`{%P&jVUOsWvG(^Q(JP8Un!hh)TVtr@~0EI zKm`|AWM73OCnWbA!5l`0;vowc-nwtU{PVBC868 zp_hNI6a{A`Buoftpx`M2BNauJHS|Xc$Q2W20!hiVhTJlhl7%X1_%9s7tWb!sl