refactor(sandbox-policy): remove capability family registries

This commit is contained in:
Tianyi Cui
2026-07-31 13:28:02 +08:00
parent e6407477a7
commit e088cfa59d
112 changed files with 260 additions and 403 deletions
@@ -12,9 +12,9 @@ The sandbox policy already enforced and logged each session's file-effect mode,
`dsh-sandbox-policy`, the owner of mode and workspace-root resolution, registers one `sandbox:policy` cache-safe context contribution. Every agent request resolves the active session directly through `ctx.sandboxPolicy.resolve({ session })`; there is no denial-history scan or process-local “last told” state.
Enforcing backends register independently disposable `filesystem`, `bash`, or `terminal` family contributions with the policy owner. The contribution names only registered families in canonical order, and is empty without one. This is current need, not a future extension: the shipped headless inheritance composition combines sandboxed filesystem tools with unfenced one-shot bash, while the persistent-tools composition combines sandboxed filesystem tools and terminal commands without a sandboxed one-shot bash executor. A blanket statement would be false in both.
The policy contribution is capability-neutral and present for every agent session. It does not maintain a second inventory of mounted backends or tools; model-visible schemas remain the authority for available operations, while the context conditions its claims on any available operation that the DSH file sandbox enforces. The [capability-neutral policy context decision](../simplification/2026-07-31-capability-neutral-sandbox-policy-context.md) supersedes the earlier family-registration mechanism while retaining this note's cache-safe delivery and durable snapshot design.
The contribution states only facts shared by every enforcement dialect for each registered family. `read-only` says those operations cannot modify files in the standing mode. Tool owners separately register a family only when their real schema and execution path offer an approved wider retry; the read-only text adds anti-refusal guidance only for the intersection of enforced and escalatable families. `workspace-write` states the canonical session workspace with non-exclusive wording and summarizes, without enumerating, that some platform temporary areas may also be writable. `danger-full-access` says the DSH file sandbox does not restrict those operations. Backend-selected temporary paths, `/dev/null`, runner readiness, and other policy domains are absent because `resolve()` cannot establish them at request assembly.
The contribution states only facts shared by every enforcement dialect. `read-only` says an available sandbox-enforced operation cannot modify files in the standing mode and directs the model to try an available tool normally, then follow any denial and escalation guidance that tool returns. `workspace-write` states the canonical session workspace with non-exclusive wording and summarizes, without enumerating, that some platform temporary areas may also be writable. `danger-full-access` says the DSH file sandbox does not restrict file modifications by available operations. Backend-selected temporary paths, `/dev/null`, runner readiness, exact tool availability, and other policy domains are absent because `resolve()` cannot establish them at request assembly.
The existing `dsh-system-prompt` assembly now has ordered dynamic contexts alongside stable system sections and tool schemas. After assembling one step, agent-loop renders all active contexts as one full snapshot with an explicit supersession statement. It appends a sourced `user/message` only when no retained snapshot exists, the bytes changed, compaction removed the retained message, or the final contribution disappeared and needs one clearing snapshot. The snapshot is appended after existing history and before `step/start`, so a changed policy preserves the preceding system-and-conversation cache prefix. The session event itself reconstructs the exact model input; `request/header` remains byte-identical when only policy context changes.
@@ -48,16 +48,16 @@ The cache-safe delivery rework then supplied a separate, non-statistical accepta
**Repeat tool schemas or plan guidance in the context.** Rejected because those surfaces already have owners and independent lifecycles. Approval current state joins the snapshot only because the same `/permission` switch changes it and leaving its system section would retain the cache defect.
**Keep Candidate A after the cache-safe move.** Rejected by the neutral real-provider task: the model returned a pure text refusal and made no tool call despite the existing bash attempt guidance. Candidate B states no escalation mechanics itself; it tells only families whose tools actually advertise a wider retry not to infer impossibility from the standing label, then delegates denial and escalation behavior back to those tool owners.
**Keep Candidate A after the cache-safe move.** Rejected by the neutral real-provider task: the model returned a pure text refusal and made no tool call despite the existing bash attempt guidance. The surviving anti-refusal principle states no escalation mechanics itself; it tells the model not to infer impossibility from the standing label, then delegates denial and escalation behavior back to the available tool.
**Keep sandbox mode absent because a standing mode label once caused preemptive refusal.** Rejected because a fresh Web request otherwise exposes mutation tools while withholding their standing policy, producing false capability claims before the first operation. The earlier live measurement remains a required counter-test: five of twelve turns ended without a tool call under `Bash commands run under the "read-only" file sandbox.` The committed tool-owned attempt guidance postdates that measurement, so the replacement is selected through a new positive-control experiment under the current tool contract rather than assuming the old and current conditions match.
**A separate model-context package.** Rejected because Cordis services can observe current runtime contributions directly and the existing assembly service can order them. A new package would add a shallow composition seam and documentation/gate surface around the same request boundary.
**A separate model-context package.** Rejected because the policy owner can resolve current session state directly and the existing assembly service can order it. A new package would add a shallow composition seam and documentation/gate surface around the same request boundary.
**Enumerate writable temporary roots.** Rejected because the backend is selected later at `confine()`: bwrap, Landlock, Seatbelt, and the in-process filesystem fence do not grant one common temporary-path set. Host-specific paths in a standing request would be both unstable and overclaimed.
## Consequences
A model can answer what registered file operations the standing mode governs before probing a tool, and the next request after `/permission` reflects the committed mode. The stable system prompt no longer changes for sandbox or approval state; a changed full context snapshot is append-only after retained history, and unchanged state adds no message. Older snapshots remain in history but are explicitly superseded by the latest full snapshot. The statement is guidance, not an enforcement guard: runtime safety still comes from the registered filesystem, one-shot bash, and terminal backends consuming the same resolved policy.
A model receives the standing file policy before probing a tool, and the next request after `/permission` reflects the committed mode. The stable system prompt no longer changes for sandbox or approval state; a changed full context snapshot is append-only after retained history, and unchanged state adds no message. Older snapshots remain in history but are explicitly superseded by the latest full snapshot. The statement is guidance, not an enforcement guard: runtime safety still comes from filesystem, one-shot bash, and terminal backends consuming the same resolved policy.
Focused tests pin all modes, family combinations, contribution disposal, canonical roots, switch timing, context ordering, clearing, stable request headers, and byte stability across different `TMPDIR` values. Keyless assembled snapshots pin the durable context message through real Loader compositions, including all three families. Keyless replay owns the neutral denial-to-escalation trajectory; it is a structural regression proof, not wording-selection evidence.
Focused tests pin all modes, canonical roots, switch timing, service disposal, context ordering, clearing, stable request headers, resume, and byte stability across different `TMPDIR` values. Keyless assembled snapshots pin the durable context message through real Loader compositions. Keyless replay owns the neutral denial-to-escalation trajectory; it is a structural regression proof, not wording-selection evidence.