Separate tool request and execution ownership

This commit is contained in:
Meta Vens 2026-07-13 05:03:30 +01:00
commit 43aed779eb
8 changed files with 41 additions and 8 deletions

View file

@ -196,6 +196,8 @@ fn run_smoke(args: Args) -> Result<Value> {
)?;
require_text(&request_text, "safe_action_family = \"repo.tool_request\"")?;
require_text(&request_text, "[request]")?;
require_text(&request_text, "routing_owner = \"Self\"")?;
require_text(&request_text, "pressure_source = \"Persona\"")?;
require_text(
&request_text,
"target_directory = \"gamecult-local/daemon-tool-directory\"",
@ -215,7 +217,7 @@ fn run_smoke(args: Args) -> Result<Value> {
"receipt_contract = \"epiphany.cultmesh.daemon_tool_invocation_receipt.v0\"",
)?;
require_text(&request_text, "host_daemon_owns_execution = true")?;
require_text(&request_text, "requester_owns_request = false")?;
require_text(&request_text, "requester_owns_execution = false")?;
require_text(&request_text, "requires_host_liveness_ready = true")?;
require_text(&request_text, "requires_cultmesh_receipts = true")?;
require_text(&request_text, "[odin]")?;

View file

@ -2721,7 +2721,8 @@ fn derive_repo_tool_request_plan(
"[request]".to_string(),
format!("id = {}", toml_basic_string(&request_id)),
format!("requester_body = {}", toml_basic_string(&requester_body)),
"requesting_agent = \"repo Persona/Self\"".to_string(),
"routing_owner = \"Self\"".to_string(),
"pressure_source = \"Persona\"".to_string(),
"target_directory = \"gamecult-local/daemon-tool-directory\"".to_string(),
"target_capability = \"daemon-tool-capability:selected-by-review\"".to_string(),
"operation = \"submitTypedToolIntent\"".to_string(),
@ -2736,7 +2737,7 @@ fn derive_repo_tool_request_plan(
toml_basic_string(receipt_contract)
),
"host_daemon_owns_execution = true".to_string(),
"requester_owns_request = false".to_string(),
"requester_owns_execution = false".to_string(),
"requires_host_liveness_ready = true".to_string(),
"requires_cultmesh_receipts = true".to_string(),
String::new(),

View file

@ -20,7 +20,10 @@ impl RepoToolRequest {
}
pub(super) fn has_request_contract(&self) -> bool {
self.request.target_directory == "gamecult-local/daemon-tool-directory"
!self.request.requester_body.is_empty()
&& self.request.routing_owner == "Self"
&& self.request.pressure_source == "Persona"
&& self.request.target_directory == "gamecult-local/daemon-tool-directory"
&& self.request.target_capability == "daemon-tool-capability:selected-by-review"
&& self.request.operation == "submitTypedToolIntent"
}
@ -30,7 +33,7 @@ impl RepoToolRequest {
&& self.cultmesh.receipt_contract
== "epiphany.cultmesh.daemon_tool_invocation_receipt.v0"
&& self.cultmesh.host_daemon_owns_execution
&& !self.cultmesh.requester_owns_request
&& !self.cultmesh.requester_owns_execution
&& self.cultmesh.requires_host_liveness_ready
&& self.cultmesh.requires_cultmesh_receipts
}
@ -55,6 +58,9 @@ impl RepoToolRequest {
#[derive(Debug, Deserialize)]
pub(super) struct RepoToolRequestBody {
requester_body: String,
routing_owner: String,
pressure_source: String,
target_directory: String,
target_capability: String,
operation: String,
@ -65,7 +71,7 @@ pub(super) struct RepoToolRequestCultMesh {
intent_contract: String,
receipt_contract: String,
host_daemon_owns_execution: bool,
requester_owns_request: bool,
requester_owns_execution: bool,
requires_host_liveness_ready: bool,
requires_cultmesh_receipts: bool,
}

View file

@ -47,6 +47,9 @@ safe_action_family = "repo.tool_request"
summary = "summary"
private_state_exposed = false
[request]
requester_body = "repo:test"
routing_owner = "Self"
pressure_source = "Persona"
target_directory = "gamecult-local/daemon-tool-directory"
target_capability = "daemon-tool-capability:selected-by-review"
operation = "submitTypedToolIntent"
@ -54,7 +57,7 @@ operation = "submitTypedToolIntent"
intent_contract = "epiphany.cultmesh.daemon_tool_invocation_intent.v0"
receipt_contract = "epiphany.cultmesh.daemon_tool_invocation_receipt.v0"
host_daemon_owns_execution = true
requester_owns_request = false
requester_owns_execution = false
requires_host_liveness_ready = true
requires_cultmesh_receipts = true
[odin]

View file

@ -1389,3 +1389,16 @@ ledger rows name the actual source Persona id as owner and retain the requested
Imagination consensus route. Bifrost owns collaboration-consensus accounting;
the Persona-to-Imagination chain is what Bifrost observes, not who Bifrost is.
Production source now contains no arrow-shaped owner value.
## Request ownership is not execution ownership
The repo tool request encoded `requesting_agent="repo Persona/Self"`, while its
typed closure ignored requester identity entirely and declared
`requester_owns_request=false`. This merged Persona pressure, Self routing, and
host execution, then denied ownership of the request because the requester did
not own execution.
The request now carries a concrete requester body, `routing_owner=Self`, and
`pressure_source=Persona`. CultMesh separately states that the target host
daemon owns execution and the requester does not. The typed closure validates
all of these fields. Persona supplies pressure; Self routes; the provider acts.

View file

@ -509,3 +509,11 @@ publishes the Persona-to-Imagination handoff separately; feedback ledger rows
use their source Persona id; Bifrost owns consensus accounting. Continue the
conceptual-substitution scan beyond owner strings: look for status, route,
receipt, or cache fields that borrow consequence authority from another organ.
The first non-owner substitution is cut in `repo.tool_request`. The old
`requesting_agent="repo Persona/Self"` and `requester_owns_request=false` shape
is replaced by requester body identity, Self routing, Persona pressure
provenance, target-host execution ownership, and requester execution denial.
The typed closure now validates the identity and authority split. Continue
looking for fields where observation, request, admission, and effect ownership
are collapsed under innocent-sounding status or cache names.

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 audit beyond owner strings. Arrow-shaped handoffs are no longer owners: Self owns intake readback, source Personas own feedback, Bifrost owns consensus accounting, and routes carry the Persona-to-Imagination handoff. Next inspect status, route, receipt, scheduler, and cache projections for borrowed consequence authority; keep closure's zero-substring invariant.
next_action: Continue the conceptual-substitution audit beyond owner strings. Tool requests now separate requester body, Self routing, Persona pressure provenance, and target-host execution; the typed closure validates the split. Next inspect status, receipt, scheduler, and cache projections for fields that collapse observation/request/admission/effect authority. Keep closure's zero-substring invariant.