Separate Unity resolution from operability

This commit is contained in:
Meta Vens 2026-07-13 17:10:44 +01:00
commit b2edd1f276
6 changed files with 33 additions and 19 deletions

View file

@ -50,7 +50,7 @@ fn run_smoke() -> Result<Value> {
require_artifact(&missing, "unity-bridge-inspection.md")?;
let exact_editor = create_fake_editor(&fake_roots, PROJECT_VERSION)?;
let ready = run_bridge(
let resolved = run_bridge(
&root,
&workspace,
&artifact_root,
@ -59,15 +59,15 @@ fn run_smoke() -> Result<Value> {
true,
)?;
require(
ready["status"] == "ready",
"exact editor should satisfy pin",
resolved["status"] == "resolved",
"exact editor path should resolve the pin",
)?;
require(
ready["editorPath"] == exact_editor.to_string_lossy().as_ref(),
resolved["editorPath"] == exact_editor.to_string_lossy().as_ref(),
"inspection should resolve exact editor",
)?;
require(
ready
resolved
.pointer("/editorBridge/exists")
.and_then(Value::as_bool)
== Some(false),
@ -89,7 +89,7 @@ fn run_smoke() -> Result<Value> {
true,
)?;
require(
planned["status"] == "ready",
planned["status"] == "resolved",
"dry run should still require exact editor",
)?;
require(
@ -148,7 +148,7 @@ fn run_smoke() -> Result<Value> {
true,
)?;
require(
bridged["status"] == "ready",
bridged["status"] == "resolved",
"bridge project should still resolve exact editor",
)?;
require(
@ -287,13 +287,13 @@ fn run_smoke() -> Result<Value> {
"workspace": workspace,
"artifactRoot": artifact_root,
"missingStatus": missing["status"],
"readyStatus": ready["status"],
"editorResolutionStatus": resolved["status"],
"plannedStatus": planned["runStatus"],
"missingPackageStatus": missing_package["status"],
"sceneProbeStatus": scene_probe["runStatus"],
"testStatus": tests["runStatus"],
"blockedStatus": blocked["runStatus"],
"resolvedEditor": ready["editorPath"],
"resolvedEditor": resolved["editorPath"],
});
write_json(&result_path, &result)?;
Ok(result)

View file

@ -223,7 +223,7 @@ fn read_unity_project_version(project_path: &Path) -> serde_json::Value {
.as_str()
.is_some_and(|value| !value.is_empty())
{
result["status"] = json!("ready");
result["status"] = json!("pinned");
} else {
result["status"] = json!("missingProjectVersion");
result["note"] = json!("ProjectVersion.txt did not contain m_EditorVersion.");
@ -364,7 +364,7 @@ fn resolve_unity_editor(project_path: &Path) -> Result<serde_json::Value> {
.collect::<Vec<_>>()
);
if let Some(exact) = exact {
result["status"] = json!("ready");
result["status"] = json!("resolved");
result["editorPath"] = json!(path_string(exact));
result["note"] = json!(format!(
"Project pins Unity {project_version}; exact editor resolved at {}.",
@ -481,7 +481,7 @@ fn build_unity_command(
Ok(command)
}
fn editor_bridge_ready(summary: &serde_json::Value) -> bool {
fn editor_bridge_present(summary: &serde_json::Value) -> bool {
summary["editorBridge"]["exists"].as_bool() == Some(true)
}
@ -619,7 +619,7 @@ fn run_unity(options: &UnityOptions) -> Result<serde_json::Value> {
summary["operation"] = json!("run");
summary["label"] = json!(options.label);
summary["unityArgs"] = json!(options.unity_args);
if summary["status"].as_str() != Some("ready") {
if summary["status"].as_str() != Some("resolved") {
summary["runStatus"] = json!("blocked");
summary["note"] = json!(format!(
"{} Runtime execution refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.",
@ -701,7 +701,7 @@ fn run_named_probe(options: &UnityOptions, operation: &str) -> Result<serde_json
"description": probe_description(operation),
"expectedArtifacts": expected_artifacts(operation),
});
if summary["status"].as_str() != Some("ready") {
if summary["status"].as_str() != Some("resolved") {
summary["runStatus"] = json!("blocked");
summary["note"] = json!(format!(
"{} Runtime/editor probe refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.",
@ -710,7 +710,7 @@ fn run_named_probe(options: &UnityOptions, operation: &str) -> Result<serde_json
write_inspection_artifacts(&directory, &summary)?;
return Ok(summary);
}
if !editor_bridge_ready(&summary) {
if !editor_bridge_present(&summary) {
let bridge_path = summary["editorBridge"]["path"]
.as_str()
.map(ToOwned::to_owned)
@ -785,7 +785,7 @@ fn test_unity(options: &UnityOptions) -> Result<serde_json::Value> {
summary["operation"] = json!("run-tests");
summary["testPlatform"] = json!(options.platform);
summary["testFilter"] = json!(options.filter);
if summary["status"].as_str() != Some("ready") {
if summary["status"].as_str() != Some("resolved") {
summary["runStatus"] = json!("blocked");
summary["note"] = json!(format!(
"{} Unity tests refused; install the exact pinned editor or use inspect artifacts as evidence of the missing runtime.",
@ -935,8 +935,8 @@ fn bridge_guidance(project_path: &Path) -> Result<String> {
"`epiphany-unity-bridge inspect --project-path {}`",
project_path.display()
);
if summary["status"].as_str() == Some("ready") {
if editor_bridge_ready(&summary) {
if summary["status"].as_str() == Some("resolved") {
if editor_bridge_present(&summary) {
Ok(format!(
"- Unity bridge: project pins {} and the exact editor resolved to `{}`. If Unity execution is needed, use named bridge operations such as `epiphany-unity-bridge probe --project-path {} --operation scene-facts --scene <Assets/...unity>` or `epiphany-unity-bridge check-compilation --project-path {}`. The bridge owns -batchmode, -quit, -projectPath, -logFile, -executeMethod, and artifacts. Do not invoke `Unity`, `Unity.exe`, default installs, or PATH-resolved editors directly, and do not refactor Unity-owned scene/prefab state as raw text when the editor bridge can inspect it.",
summary["projectVersion"].as_str().unwrap_or("unknown"),

View file

@ -1487,3 +1487,12 @@ The Rider bridge called a discovered executable path, solution file, and Git
branch `ready` without executing or validating any of them. These projections
now report `discovered`, `found`, and `gitDetected`. Discovery owns topology;
only an actual launch receipt may later claim operability.
## Editor resolution is not editor operability
Unity inspection called a parsed project version and an existing exact-editor
path `ready`; its smoke proved that state with a fake text file named as the
editor executable. The project version is now `pinned`, the editor path is
`resolved`, and editor-bridge package existence is explicitly `present`.
Commands may use resolution to choose what to execute, but only `runStatus`
records planned, completed, failed, or blocked execution consequence.

View file

@ -568,3 +568,8 @@ binary tests, and the all-bin check pass.
Rider bridge topology no longer claims readiness from path existence. Rider is
`discovered`, solutions are `found`, and repository topology is `gitDetected`;
the native bridge smoke passes with the renamed evidence states.
Unity bridge uses the same boundary: project version parsing is `pinned`, exact
editor path discovery is `resolved`, and the editor package predicate is named
`present`. Operability belongs to the subsequent `runStatus` receipt. The
native Unity bridge smoke, 259 library tests, and all-bin check pass.

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 observation-to-consequence audit. Heartbeat, operator-cache, and Rider topology projections no longer turn presence into readiness; Rider paths are discovered, solutions found, and Git topology detected. Next inspect provider-capability and editor-bridge projections for discovery or configuration standing in for verified operability. Keep closure's zero-substring invariant.
next_action: Continue the observation-to-consequence audit. Heartbeat, operator-cache, Rider, and Unity projections no longer turn presence, parsing, or path resolution into readiness; Unity operability belongs to `runStatus`. Next inspect provider-capability/configuration projections and Persona bridge mouths for credentials or advertised surfaces standing in for verified transport. Keep closure's zero-substring invariant.