Separate doctrine update authority

This commit is contained in:
Meta Vens 2026-07-13 04:20:56 +01:00
commit e48e2ad521
8 changed files with 193 additions and 59 deletions

View file

@ -222,7 +222,13 @@ fn run_smoke(args: Args) -> Result<Value> {
)?;
require_text(&request_text, "[request]")?;
require_text(&request_text, "status = \"awaiting-doctrine-review\"")?;
require_text(&request_text, "requested_owner = \"Maintainer/Mind\"")?;
require_text(&request_text, "routing_owner = \"Self\"")?;
require_text(
&request_text,
"required_reviewers = [\"Maintainer\", \"Mind\", \"Soul\"]",
)?;
require_text(&request_text, "doctrine_admission_owner = \"Mind\"")?;
require_text(&request_text, "mutation_owner = \"Hands\"")?;
require_text(
&request_text,
"requested_effect = \"review-repo-agent-doctrine-update\"",
@ -276,10 +282,9 @@ fn run_smoke(args: Args) -> Result<Value> {
require_text(&request_text, "service_lifecycle_authority = false")?;
require_text(&request_text, "cross_body_mutation_authorized = false")?;
require_text(&request_text, "private_verse_rummaging = false")?;
require_text(
&request_text,
"maintainer_or_mind_doctrine_authority_required = true",
)?;
require_text(&request_text, "maintainer_review_required = true")?;
require_text(&request_text, "mind_admission_required = true")?;
require_text(&request_text, "hands_receipts_required = true")?;
require_text(&request_text, "private_state_exposed = false")?;
require_text(
&agents_text,

View file

@ -5123,7 +5123,10 @@ fn derive_repo_doctrine_update_request_plan(
"[request]".to_string(),
format!("id = {}", toml_basic_string(&request_id)),
"status = \"awaiting-doctrine-review\"".to_string(),
"requested_owner = \"Maintainer/Mind\"".to_string(),
"routing_owner = \"Self\"".to_string(),
"required_reviewers = [\"Maintainer\", \"Mind\", \"Soul\"]".to_string(),
"doctrine_admission_owner = \"Mind\"".to_string(),
"mutation_owner = \"Hands\"".to_string(),
"requested_effect = \"review-repo-agent-doctrine-update\"".to_string(),
format!("doctrine_target = {}", toml_basic_string(doctrine_target)),
"change_surface = \"repo-local agent instructions and operating doctrine\"".to_string(),
@ -5163,7 +5166,9 @@ fn derive_repo_doctrine_update_request_plan(
"service_lifecycle_authority = false".to_string(),
"cross_body_mutation_authorized = false".to_string(),
"private_verse_rummaging = false".to_string(),
"maintainer_or_mind_doctrine_authority_required = true".to_string(),
"maintainer_review_required = true".to_string(),
"mind_admission_required = true".to_string(),
"hands_receipts_required = true".to_string(),
String::new(),
"[verification]".to_string(),
"asks = [".to_string(),
@ -7194,106 +7199,73 @@ fn closure_family_assertions(
);
}
"repo.doctrine_update_request" => {
let request = parse_repo_doctrine_update_request(&content).ok();
push_assertion(
&mut assertions,
"doctrine-update-request-schema-present",
content.contains("schema_version = \"epiphany.repo_doctrine_update_request.v0\""),
request
.as_ref()
.is_some_and(RepoDoctrineUpdateRequest::has_canonical_identity),
"Committed doctrine update request carries the schema version.".to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-family-present",
content.contains("safe_action_family = \"repo.doctrine_update_request\""),
request
.as_ref()
.is_some_and(RepoDoctrineUpdateRequest::has_canonical_identity),
"Committed doctrine update request carries the safe action family.".to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-summary-present",
content.contains(&compact_summary),
request
.as_ref()
.is_some_and(|value| value.summary == compact_summary),
"Committed doctrine update request contains the accepted pressure summary."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-awaits-review",
content.contains("[request]")
&& content.contains("status = \"awaiting-doctrine-review\"")
&& content.contains("requested_owner = \"Maintainer/Mind\"")
&& content.contains(
"requested_effect = \"review-repo-agent-doctrine-update\"",
)
&& content.contains("doctrine_target = \"AGENTS.md\"")
&& content.contains("requires_source_grounding = true")
&& content.contains("requires_human_or_maintainer_review = true"),
request.as_ref().is_some_and(RepoDoctrineUpdateRequest::has_coherent_routing),
"Committed doctrine update request waits for Mind/maintainer review before doctrine consequence."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-antecedents-present",
content.contains("[antecedents]")
&& content.contains("persona_or_human_feedback_required = true")
&& content.contains("imagination_plan_required = true")
&& content.contains("mind_adoption_required = true")
&& content.contains("soul_review_required = true")
&& content.contains("maintainer_review_required = true"),
request.as_ref().is_some_and(RepoDoctrineUpdateRequest::has_antecedent_contract),
"Committed doctrine update request requires feedback, Imagination, Mind, Soul, and maintainer antecedents."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-receipt-contract",
content.contains("[required_receipts]")
&& content.contains(
"imagination_plan = \"epiphany.repo_work_imagination_action_items_receipt.v0\"",
)
&& content.contains(
"mind_adoption = \"epiphany.repo_work_mind_adoption_decision.v0\"",
)
&& content.contains(
"soul_review = \"epiphany.repo_work_closure_review.v0\"",
)
&& content.contains(
"maintainer_review = \"gamecult.maintainer.review_receipt.v0\"",
)
&& content.contains("hands_commit = \"epiphany.hands.commit_receipt\""),
request.as_ref().is_some_and(RepoDoctrineUpdateRequest::has_receipt_contract),
"Committed doctrine update request names Imagination, Mind, Soul, maintainer, and Hands receipts."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-packet-contract",
content.contains("[doctrine_packet]")
&& content.contains("requires_current_doctrine_ref = true")
&& content.contains("requires_proposed_change_summary = true")
&& content.contains("requires_invariant_impact = true")
&& content.contains("requires_rehydration_impact = true")
&& content.contains("requires_rollback_plan = true")
&& content.contains("requires_private_state_redaction_check = true"),
request.as_ref().is_some_and(RepoDoctrineUpdateRequest::has_packet_contract),
"Committed doctrine update request names doctrine diff, invariant, rehydration, rollback, and redaction requirements."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-authority-seals",
content.contains("[authority]")
&& content.contains("direct_doctrine_mutation_authority = false")
&& content.contains("direct_hands_authority = false")
&& content.contains("direct_mind_state_commit = false")
&& content.contains("publication_authorized = false")
&& content.contains("merge_authorized = false")
&& content.contains("service_lifecycle_authority = false")
&& content.contains("cross_body_mutation_authorized = false")
&& content.contains("private_verse_rummaging = false")
&& content
.contains("maintainer_or_mind_doctrine_authority_required = true"),
request.as_ref().is_some_and(RepoDoctrineUpdateRequest::has_authority_seals),
"Committed doctrine update request denies doctrine/action/state/publication/service/cross-body authority."
.to_string(),
);
push_assertion(
&mut assertions,
"doctrine-update-request-private-seal",
content.contains("private_state_exposed = false"),
request
.as_ref()
.is_some_and(|value| !value.private_state_exposed),
"Committed doctrine update request preserves the private-state seal.".to_string(),
);
}
@ -13978,6 +13950,7 @@ idunn_deployment_authority_required = true
"repo.artifact_acceptance_request",
"repo.metrics_request",
"repo.readiness_review_request",
"repo.doctrine_update_request",
"repo.secret_policy_request",
"repo.dependency_policy_request",
"repo.deployment_config",
@ -14032,6 +14005,35 @@ idunn_deployment_authority_required = true
);
}
#[test]
fn doctrine_comment_cannot_counterfeit_hands_authority_seal() {
let text = r#"
schema_version = "epiphany.repo_doctrine_update_request.v0"
safe_action_family = "repo.doctrine_update_request"
summary = "summary"
private_state_exposed = false
[request]
status = "awaiting-doctrine-review"
routing_owner = "Self"
required_reviewers = ["Maintainer", "Mind", "Soul"]
doctrine_admission_owner = "Mind"
mutation_owner = "Hands"
requested_effect = "review-repo-agent-doctrine-update"
doctrine_target = "AGENTS.md"
requires_source_grounding = true
requires_human_or_maintainer_review = true
[antecedents]
[required_receipts]
[doctrine_packet]
[authority]
direct_doctrine_mutation_authority = false
# direct_hands_authority = false
direct_hands_authority = true
"#;
let request = parse_repo_doctrine_update_request(text).expect("fixture is typed TOML");
assert!(!request.has_authority_seals());
}
#[test]
fn modeling_finding_round_trips_and_refuses_private_state() -> Result<()> {
let temp = tempfile::tempdir()?;

View file

@ -549,3 +549,110 @@ impl RepoDeploymentRequest {
pub(super) fn parse_repo_deployment_request(text: &str) -> Result<RepoDeploymentRequest> {
toml::from_str(text).context("deployment request is not valid typed TOML")
}
#[derive(Debug, Deserialize)]
pub(super) struct RepoDoctrineUpdateRequest {
pub(super) schema_version: String,
pub(super) safe_action_family: String,
pub(super) summary: String,
pub(super) private_state_exposed: bool,
request: RepoDoctrineRequestBody,
antecedents: std::collections::BTreeMap<String, bool>,
required_receipts: std::collections::BTreeMap<String, String>,
doctrine_packet: std::collections::BTreeMap<String, bool>,
authority: std::collections::BTreeMap<String, bool>,
}
impl RepoDoctrineUpdateRequest {
pub(super) fn has_canonical_identity(&self) -> bool {
self.schema_version == "epiphany.repo_doctrine_update_request.v0"
&& self.safe_action_family == "repo.doctrine_update_request"
}
pub(super) fn has_coherent_routing(&self) -> bool {
let r = &self.request;
r.status == "awaiting-doctrine-review"
&& r.routing_owner == "Self"
&& r.required_reviewers == ["Maintainer", "Mind", "Soul"]
&& r.doctrine_admission_owner == "Mind"
&& r.mutation_owner == "Hands"
&& r.requested_effect == "review-repo-agent-doctrine-update"
&& r.doctrine_target == "AGENTS.md"
&& r.requires_source_grounding
&& r.requires_human_or_maintainer_review
}
pub(super) fn has_antecedent_contract(&self) -> bool {
[
"persona_or_human_feedback_required",
"imagination_plan_required",
"mind_adoption_required",
"soul_review_required",
"maintainer_review_required",
]
.iter()
.all(|key| self.antecedents.get(*key) == Some(&true))
}
pub(super) fn has_receipt_contract(&self) -> bool {
[
(
"imagination_plan",
"epiphany.repo_work_imagination_action_items_receipt.v0",
),
(
"mind_adoption",
"epiphany.repo_work_mind_adoption_decision.v0",
),
("soul_review", "epiphany.repo_work_closure_review.v0"),
("maintainer_review", "gamecult.maintainer.review_receipt.v0"),
("hands_commit", "epiphany.hands.commit_receipt"),
]
.iter()
.all(|(key, value)| self.required_receipts.get(*key).is_some_and(|v| v == value))
}
pub(super) fn has_packet_contract(&self) -> bool {
[
"requires_current_doctrine_ref",
"requires_proposed_change_summary",
"requires_invariant_impact",
"requires_rehydration_impact",
"requires_rollback_plan",
"requires_private_state_redaction_check",
]
.iter()
.all(|key| self.doctrine_packet.get(*key) == Some(&true))
}
pub(super) fn has_authority_seals(&self) -> bool {
[
"direct_doctrine_mutation_authority",
"direct_hands_authority",
"direct_mind_state_commit",
"publication_authorized",
"merge_authorized",
"service_lifecycle_authority",
"cross_body_mutation_authorized",
"private_verse_rummaging",
]
.iter()
.all(|key| self.authority.get(*key) == Some(&false))
&& [
"maintainer_review_required",
"mind_admission_required",
"hands_receipts_required",
]
.iter()
.all(|key| self.authority.get(*key) == Some(&true))
}
}
#[derive(Debug, Deserialize)]
struct RepoDoctrineRequestBody {
status: String,
routing_owner: String,
required_reviewers: Vec<String>,
doctrine_admission_owner: String,
mutation_owner: String,
requested_effect: String,
doctrine_target: String,
requires_source_grounding: bool,
requires_human_or_maintainer_review: bool,
}
pub(super) fn parse_repo_doctrine_update_request(text: &str) -> Result<RepoDoctrineUpdateRequest> {
toml::from_str(text).context("doctrine update request is not valid typed TOML")
}

View file

@ -1232,6 +1232,19 @@ trees.
Direct source inventory now reports 56 substring occurrences across seven
remaining families.
## Reviewer OR-gate as doctrine authority
`repo.doctrine_update_request` named `Maintainer/Mind` as one owner and
required `maintainer_or_mind` doctrine authority, collapsing review and
admission into an ambiguous OR gate. Typed operations closure now requires the
actual chain: Imagination authors, Self routes, Maintainer reviews, Soul
verifies, Mind admits doctrine state, and Hands performs the `AGENTS.md`
mutation under receipts. A comment cannot counterfeit Hands denial.
All remaining substring-backed families are presentation-only. Direct source
inventory is 17 occurrences across worklog, planning/checklist notes,
managed/status sections, and task cards.
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.

View file

@ -56,6 +56,7 @@
- Repo collaboration policy/topic files are proposals, not live social or renderer contracts. Imagination authors them, Persona owns discussion, Persona/Mind review policy, Mind admits repo policy, Bifrost owns publication, and requested public-room/Eve-surface identifiers remain unpublished until provider receipts exist. Epiphany does not own downstream Eve/TUI/GUI composition.
- The deterministic `repo.tool_capabilities` and `repo.eve_surface` safe families are deleted. Expected tool ids and invented surface/row/lowering catalogs cannot substitute for host advertisements or provider-published composition graphs. Live paths are typed tool requests/host receipts, Odin discovery, Eve connection receipts, and provider-owned publication.
- `repo.body_manifest` is deleted. An unconsumed `epiphany.toml` that invents Body/Verse/Eve identity and capability hints is not observed runtime state or admitted birth configuration. Runtime state, repo birth receipts, and provider advertisements own those facts.
- Doctrine update requests split authority: Imagination authors, Self routes, Maintainer reviews, Soul verifies, Mind admits doctrine state, and Hands mutates `AGENTS.md` under receipts. There is no Maintainer/Mind composite owner or OR gate.
- `repo.planning_brief` is deleted. It contained no candidate work-item records and let copied global catalog/schema/closure constants self-attest as per-item planning and readiness evidence. The live preparation chain is consensus draft -> interpretation request -> objective draft -> Mind adoption.
- There is no aggregate repo-swarm MVP gate. The former smoke manually aggregated fixture summaries into green rows and `demoReady=true`; it and its wrapper are deleted. Whole-organism readiness requires a live-fire path whose claimed consequences are observed at their owning boundaries.
- Persona owner: `collaboration-feedback` writes public, bounded feedback only after that provider receipt exists.

View file

@ -406,6 +406,12 @@ belong to collaboration policy and the deleted manifest family is rejected.
The recursive legacy smoke timed out and spawned nested Cargo trees, so it was
not treated as proof. Direct inventory is 56 occurrences across seven families.
Doctrine review no longer has a `Maintainer/Mind` composite owner or OR gate.
Imagination authors, Self routes, Maintainer reviews, Soul verifies, Mind admits
doctrine state, and Hands mutates `AGENTS.md` under receipts. Typed operations
closure and a counterfeit-Hands fixture protect the chain. The remaining
source inventory is 17 occurrences across six presentation families.
Consensus Brief keeps its honest draft/unconverged semantics but no longer
proves them with substring presence. Preparation owns its typed consensus,
Imagination route, inputs, authority, and privacy closure; a malicious comment

Binary file not shown.

View file

@ -660,4 +660,4 @@ current_status:
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. swarm overview readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
next_action: Continue the conceptual-substitution pass. repo.body_manifest is deleted because unconsumed epiphany.toml invented Body/Verse/Eve identity and capability hints; runtime state, repo birth receipts, and provider advertisements remain authoritative. A direct generator test proves collaboration policy owns its proposal fields and the deleted manifest family is rejected. Direct source inventory reports 56 content.contains occurrences across seven families. Next map repo.doctrine_update_request ownership and decide whether deterministic AGENTS.md cargo is an honest Maintainer/Mind proposal or another adopted-law fiction. The six tiny presentation families should be typed or demoted only after doctrine authority is clean.
next_action: Finish the closure conceptual-substitution pass. Doctrine requests now split Imagination authoring, Self routing, Maintainer review, Soul verification, Mind admission, and Hands mutation; no composite owner or OR gate remains. Every consequence-bearing closure family is typed or deleted. Direct source inventory is 17 content.contains occurrences across six presentation-only families: worklog, planning/checklist notes, managed/status sections, and task cards. Classify these explicitly as presentation-only and either type their small structural contracts or remove closure assertions that pretend presentation text is authority. Then audit beyond closure for remaining borrowed-owner strings and false provider mirrors.