Repository navigation
evidence scope: grant written on one runtime root, enforced from another #6865
Description
What happens
An operator's evidence-scope grant is written (and read back for the UI) on
controller.coordination_runtime_root, but the manager read path resolves its
policy from resolve_runtime_root(registry, --runtime-root). When the registry
declares a different common_runtime_root than the --runtime-root override,
the grant lands on one root and is enforced from another.
Write side: loopx/chat_lark_api.py:557, :721 (the same value is returned by
chat_runtime.py:238 via coordination_runtime_root(registry_path, store.root.parent),
which ignores the CLI override).
Read side: loopx/chat_server.py:1720-1722 and :1767 pass the override-resolved
root into the inspection/read path.
Observed (snapshot 105c4bc1, still the same code on main at 1c7b258cb)
Using a registry with common_runtime_root=$Y and two goals (alpha, beta),
then configuring all_registered while running with --runtime-root $X:
CONFIGURE {"ok":true,"executed":true,"goal_scope":"all_registered","evidence_goal_ids":["alpha","beta"]}
POLICY at $Y: evidence_goal_scope=all_registered
READBACK_WRITE {"goal_scope":"all_registered","goal_ids":["alpha","beta"]}
READBACK_READ {"goal_scope":"selected","goal_ids":null}
GRANT_READ null
CONTEXT_MANAGER_SEES {"goal_ids":["alpha"],"warnings":[]}
The connect response is ok:true, the settings surface reads back
all_registered with both ids, and the manager's turn context silently shows
only the connection's own goal, with warnings: []. Nothing distinguishes
"grant written on another root" from "never configured".
The mirror direction is also possible and worse: if $X already holds an older
grant, the operator's narrower selection is written to $Y while reads keep
honouring the stale broader grant at $X. I only reproduced the narrowing
direction; I did not construct the widening state.
Why the existing verification misses it
configure_evidence_scope re-runs the same planner for its "read scope
verification" (loopx/capabilities/manager_context/__init__.py:359-364); it never
consults the observer the readers use. The contract test
(tests/test_chat_lark_api_contract.py:766-861) sets the controller root equal to
the session root and never passes an override, so both roots coincide by
construction.
Expected
Writing a grant on one root and enforcing it from another should either be
impossible (one resolver for write, readback and read) or loud (a warning/error
that the configured grant was not found at the read root).
Suggested fix direction
Resolve the policy root once, from the same function the read path uses
(resolve_runtime_root), and pass it to the configuration/readback path; or fail
the connect/configure call when the write root and the read root differ and no
grant exists at the read root.
Control
With --runtime-root removed (or a registry without common_runtime_root) both
roots collapse and the same steps report the grant correctly. Repro is
deterministic, local, no model calls.
Activity
JasonBuildAI commentedon Oct 19, 2026
Claiming this issue for a focused fix.
Plan: reproduce the mismatch on current
main, then make configure/readback and manager reads use one policy-runtime-root resolver, with a regression test that runs a registrycommon_runtime_rootdifferent from the--runtime-rootoverride. I’ll open a PR againstloopx-project/loopx:mainthat links this issue and include before/after evidence.AI disclosure: this investigation and patch are drafted with AI assistance at my request, under my direction and review.
JasonBuildAI commentedon Oct 19, 2026
Implemented in #6893.
The manager scope now reads the evidence policy from the same
ChatRuntimeController.coordination_runtime_rootused by configure/readback, while the configured manager executor still resolves from the Chat override root. The regression exercises the realserve_chatentrypoint withsteward_executor=dshon the override root and covers both the observed hiddenall_registeredgrant and the mirrored stale broader grant.Final validation on head
edc9b921f: focused regression 1 passed (fails against the old read root and against collapsed executor roots), related suites 124 + 89 + 38 passed, Ruff clean. Model-backed Lark/native-steward suites are blocked on this host byCodexChatAgentError: executable_not_found, reproduced at the clean upstream base.