Observed authority drift
At commit f3475cf000a379732d37cfb317754dec5ef2b4db, registry admission mutates several global environments with assign() and silently replaces manifest, resource, resolver, term-set, and operation identifiers.
This allows an already registered resource to acquire a resolver version it never declared. Executed reproduction:
rho_register_manifest(reg, manifest_with_resolver("shared", "1.0", resource = "r1"))
rho_bind_resolver_impl(reg, "shared", impl)
rho_register_manifest(reg, manifest_with_resolver("shared", "2.0", resource = "r2"))
receipt <- rho_await(rho_resolve_resource(reg, "r1"), timeout = 1000)
Observed:
resource=r1 resolver_version=2.0
The receipt for r1 therefore reports a resolver spec introduced by another manifest after r1 was admitted. The registry no longer preserves the declaration that is supposed to authorize and explain resolution.
There is a second effect-contract failure in the same path: rho_resolve_resource() calls impl(resource, ctx) synchronously before wrapping its output. A resolver containing a one-second blocking effect made the public call itself take 1.01 seconds before returning a RhoTask.
Required contract
- Validate a complete manifest admission before mutating registry state.
- Reject duplicate identifiers within a manifest and collisions with admitted manifests unless replacement is an explicit typed operation.
- Preserve manifest identity, resolver identity/version, and resource declaration together so a later registration cannot rewrite an earlier receipt.
- Define replacement lineage and rebinding semantics explicitly; do not leave existing resources attached accidentally to a new spec or old implementation.
- Start resolver invocation behind the returned task. A selected execution binding owns placement and cancellation;
rho_resolve_resource() must return before resolver effects run.
- Inject receipt time through the resolution context rather than discovering a semantic clock inside receipt construction.
- Make failed admission atomic: no partial manifest/resource/resolver entries remain.
Acceptance evidence
- same-manifest duplicate identifiers fail before mutation;
- cross-manifest resource/resolver/operation collisions fail closed;
- the reproduced
r1 receipt remains bound to shared@1.0;
- an explicitly replaced resolver records old and new authority and has tested behavior for existing resources;
- a blocking fixture proves
rho_resolve_resource() returns a task promptly;
- cancellation and resolver failure remain typed;
- deterministic-clock and partial-admission fixtures pass.
Prepared in Pi using GPT Sol 5.6 High; both reproductions were executed locally against the stated commit.
Observed authority drift
At commit
f3475cf000a379732d37cfb317754dec5ef2b4db, registry admission mutates several global environments withassign()and silently replaces manifest, resource, resolver, term-set, and operation identifiers.This allows an already registered resource to acquire a resolver version it never declared. Executed reproduction:
Observed:
The receipt for
r1therefore reports a resolver spec introduced by another manifest afterr1was admitted. The registry no longer preserves the declaration that is supposed to authorize and explain resolution.There is a second effect-contract failure in the same path:
rho_resolve_resource()callsimpl(resource, ctx)synchronously before wrapping its output. A resolver containing a one-second blocking effect made the public call itself take 1.01 seconds before returning aRhoTask.Required contract
rho_resolve_resource()must return before resolver effects run.Acceptance evidence
r1receipt remains bound toshared@1.0;rho_resolve_resource()returns a task promptly;Prepared in Pi using GPT Sol 5.6 High; both reproductions were executed locally against the stated commit.