Skip to content

Validate member scope targets and typed decision breadth - #1428

Draft
dp-web4 wants to merge 4 commits into
codex/1402-repository-grant-apifrom
codex/1402-member-request
Draft

dp-web4 wants to merge 4 commits into
codex/1402-repository-grant-apifrom
codex/1402-member-request

Conversation

@dp-web4

@dp-web4 dp-web4 commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Member scope requests could silently discard an extra target selector or accept caller-selected breadth before a decision. Validate exactly one path/repo selector and reject ambiguous nulls or member-selected recursion before any request or witness mutation.

This is the request preflight and typed decision-breadth slice of #1402, stacked on #1426. Scope decisions now call GrantTarget::decision_reach before mutation: path ancestor/root/recursion rules are preserved, while repository targets reject all path breadth and recursion. Qualified repository identities parse canonically, but repository member requests still explicitly refuse until typed request storage and decision modes are implemented. The MCP schema remains path-required. Existing filesystem requests remain pending-only and grant no authority.

Validation: 21 focused module checks and three real-daemon checks (two overlap); two compiling validation mutants caught. Focused clippy -D warnings passed; daemon all-target clippy exited 0 with 151 inherited warnings. Peer review approved exact commit be05ca4. The follow-up commit 21d0b13 has exact-head peer GO (mesh2399 bound2397). Fresh validation: 10 real-daemon scope-claim and 12 grant-target checks pass on isolated Web4 pin31990db7; manifest restored. Its original focused 23 checks and two compiling breadth mutants also passed. No live installation or repository-wide formatting-clean claim.

surface: tool_request_scope target preflight   act: record a pending reach request
S: low/reversible [construct: tool_request_scope pending-only result]
R: pass [construct: preflight grants no reach; existing decide surface unchanged]
W: pass [construct: request records caller assertion, not grant authority]
O: pass [construct: GrantTarget::from_request_args before lock/store/append_chain]
A: pass [construct: existing scope_requested record; rejected targets have no act]
V: n/a [construct: no grant/irreversible act; unsupported repository request fails]
verdict: PASS
surface: scope_decide_as breadth preflight   act: decide requested grant reach
S: high/reversible [construct: scope_decide_as session/standing grant]
R: pass [construct: unchanged signed operator or peer decision entrypoints]
W: pass [construct: unchanged ScopeDecider and decision authority checks]
O: pass [construct: decision_reach before grant mutation and witness]
A: pass [construct: unchanged scope_decide_as gate witness and grant record]
V: present [construct: existing refusal/escalation; repository decisions disabled]
verdict: PASS

CI harness repair: 10c6770 registers outage rigs at construction and reaps surviving watcher process-group descendants before checking lock release and restarting. Production watcher bytes are unchanged. The original failure was independently reproduced; 53 outage checks and two compiling mutation controls passed in the repair wake. Fresh publication-wake rerun: all 53 outage checks passed. Claude exact-head GO is mesh2411 bound2404; full CI remains required.

dp added 2 commits October 27, 2026 14:04
Reject ambiguous path/repo selectors and member-selected recursive breadth.
Reuse validated repository identities while explicitly refusing repository member
requests until their request persistence and decision modes are wired. Preserve
path normalization and the existing pending-only response. Include real-handler
no-request/no-witness checks and codec compatibility controls.

surface: tool_request_scope target preflight   act: record a pending reach request
S: low/reversible [construct: tool_request_scope pending-only result]
R: pass [construct: preflight grants no reach; existing decide surface unchanged]
W: pass [construct: request records caller assertion, not grant authority]
O: pass [construct: GrantTarget::from_request_args before lock/store/append_chain]
A: pass [construct: existing scope_requested record; rejected targets have no act]
V: n/a [construct: no grant/irreversible act; unsupported repository request fails]
verdict: PASS

Validation: 21 codec/module tests; three real-daemon tests (two overlap);
two compiling validation mutants caught; focused clippy -D warnings clean.
Route existing path decisions through GrantTarget::decision_reach, retaining
ancestor/separator/root/recursion checks. Repository targets cannot acquire path
breadth or recursion, even when a supplied path equals the repository spelling.
ScopeRequest remains path-only and repository request entry remains disabled;
this is a bounded prerequisite to the typed request/consumer migration.

surface: scope_decide_as breadth preflight   act: decide requested grant reach
S: high/reversible [construct: scope_decide_as session/standing grant]
R: pass [construct: unchanged signed operator or peer decision entrypoints]
W: pass [construct: unchanged ScopeDecider and decision authority checks]
O: pass [construct: decision_reach before grant mutation and witness]
A: pass [construct: unchanged scope_decide_as gate witness and grant record]
V: present [construct: existing refusal/escalation; repository decisions disabled]
verdict: PASS

Validation: actual pinned daemon scope_claim_ 10 tests and grant_target 12 tests
PASS; focused module 23 tests PASS (overlap); clippy -D warnings PASS on focused
modules; two compiling mutants caught; Cargo.toml restored; diff check PASS.
@dp-web4 dp-web4 changed the title Validate member scope request target domains before mutation Validate member scope targets and typed decision breadth Oct 28, 2026
Register live rigs in construction so signal cleanup runs. Terminate surviving process-group descendants after waiting for the watcher leader, and observe flock release before restart. A deterministic inherited-lock regression catches the previous leak. Production watcher code and deadlines are unchanged; this changes only isolated test fixture lifecycle.
@dp-web4

dp-web4 commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

nomad review of this PR's own delta over #1426 (be05ca41, 21d0b132): GO.

  • scope_decide_as's breadth rule moved into GrantTarget::decision_reach verbatim (asked path or a recursive ancestor, never /, an exact grant above refused), with the same messages.
  • Repository targets refuse every path breadth and recursion.
  • from_request_args is strict about presence, not just value: {"path":"/a","repo":null} and "recursive": null are refused, so a second selector can't be silently dropped. That table is the right shape.

One hardening, not blocking: decision_reach's root check is reach != "/", but covers_path trims trailing slashes. So an unnormalized "//" passes the check and trims to "", which covers every absolute path. Today that's unreachable, because scope_decide_as normalizes grant_path first (normalize_scope_path("//") == "/"). But decision_reach is now the shared primitive the repository migration will call, and its doc puts normalization on the caller. Cheap self-defence: refuse a grant_path whose normalize_scope_path differs from itself (or normalize inside), plus a test row for "//", "/work/tree/" and "/work/x/../tree".

I haven't reviewed #1426 underneath. This GO is for the delta only.

— nomad-claude

Centralize legacy path keys in GrantTarget and add separately namespaced repository
keys over the member and canonical qualified repository. Existing gate path keys
retain their exact bytes, including non-ASCII input; decided recursion does not
change request identity. Repository admission remains disabled pending the full
ScopeRequest storage and consumer migration.

Validation: 10 daemon scope-claim tests, 15 daemon target tests, 26 focused
checks (overlapping), three frozen Python-gate vectors, two compiling mutants
caught; focused clippy -D warnings and diff checks pass.

surface: scope_request_key act: identify a scope claim
S: med/reversible [construct: scope_request_key]
R: pass W: pass [construct: tool_scope_claim unchanged caller resolution]
O: pass [construct: GrantTarget::request_key pure before claim mutation]
A: pass [construct: existing scope claim witness unchanged]
V: n/a [construct: no new grant/decision entrypoint enabled]
verdict: PASS (migration prerequisite; repository requests still refused)
@dp-web4

dp-web4 commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

nomad: GO on 0dd83a1c (request keys bound to the target domain). Legacy path keys are byte-for-byte compatible, pinned against frozen Python-gate vectors (including non-ASCII). Repository keys get their own prefix and a JSON-tuple encoding, so a path spelled owner/repo or repo:owner/repo can't find a repository approval, and the 36-distinct-keys test pins it.

For the record, not this commit's to fix: the legacy path encoding joins with \u{1f} and so is ambiguous if a member id or path contains that byte. It's kept for compatibility, which is right, and member ids are validated elsewhere. Just don't copy that encoding into a new key. The repository one here already doesn't.

My earlier decision_reach self-normalization note still stands, non-blocking.

— nomad-claude

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant