# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-a-capability-scoped-tool-sandbox-has-silently-widened-its-authority-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-a-capability-scoped-tool-sandbox-has-silently-widened-its-authority-boundary.warrant.json
# Markdown SHA-256:   a8d1bb9d7a4cc4887f74303799741f6cbf2a5b31c0f9160cf94d6bf18cfbebb1
# Code AST SHA-256:   4ab1b8cc00c4bda0b1bcfcd35c2628e76d3d56bebd4930f791d7922e128b78eb
# Verified Classes:   CapabilityAccrualError, Capability
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import os
from dataclasses import dataclass


class CapabilityAccrualError(RuntimeError):
    """Raised when a capability's exercised authority exceeds its granted scope."""
    pass


@dataclass(frozen=True)
class Capability:
    root_inode: int          # object identity captured at grant time
    root_path: str           # declared scope, for diagnostics only
    read_only: bool = True


def resolve_inode(path: str) -> int:
    # Real resolution: follows symlinks, reads live namespace state.
    return os.stat(path, follow_symlinks=True).st_ino


def read_file(cap: Capability, requested_path: str) -> bytes:
    # The lexical check the naive sandbox performs.
    if not os.path.abspath(requested_path).startswith(cap.root_path):
        raise CapabilityAccrualError(
            f"lexical scope violation: {requested_path} outside {cap.root_path}"
        )
    # The check that actually matters: object identity at use time.
    actual_inode = resolve_inode(requested_path)
    if actual_inode != cap.root_inode:
        raise CapabilityAccrualError(
            f"capability accrual: {requested_path} resolves to inode "
            f"{actual_inode}, granted root is {cap.root_inode} "
            f"({cap.root_path}) — namespace substitution detected"
        )
    with open(requested_path, "rb") as fh:
        return fh.read()
