Skip to content

evidence scope: grant written on one runtime root, enforced from another #6865

Closed

Description

@BiBoyang

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

  1. JasonBuildAI commented on Oct 19, 2026

    @JasonBuildAI
    Contributor

    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 registry common_runtime_root different from the --runtime-root override. I’ll open a PR against loopx-project/loopx:main that 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.

  2. JasonBuildAI commented on Oct 19, 2026

    @JasonBuildAI
    Contributor

    Implemented in #6893.

    The manager scope now reads the evidence policy from the same ChatRuntimeController.coordination_runtime_root used by configure/readback, while the configured manager executor still resolves from the Chat override root. The regression exercises the real serve_chat entrypoint with steward_executor=dsh on the override root and covers both the observed hidden all_registered grant 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 by CodexChatAgentError: executable_not_found, reproduced at the clean upstream base.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions